手写 Spring 事务、IOC、DI 和 MVC
作者:互联网
Spring AOP 原理
什么是 AOP?
AOP 即面向切面编程,利用 AOP 可以对业务进行解耦,提高重用性,提高开发效率
应用场景:日志记录,性能统计,安全控制,事务处理,异常处理
AOP 底层实现原理是采用代理实现的
Spring 事务
基本特性:
- 原子性
- 隔离性
- 一致性
- 持久性
事务控制分类:
编程式事务:手动控制事务操作
声明式事务:通过 AOP 控制事务
编程式事务实现
使用编程事务实现手动事务
@Component
@Scope("prototype")
public class TransactionUtils {
// 获取事务源
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
// 开启事务
public TransactionStatus begin() {
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
return transaction;
}
// 提交事务
public void commit(TransactionStatus transaction) {
dataSourceTransactionManager.commit(transaction);
}
// 回滚事务
public void rollback(TransactionStatus transaction) {
dataSourceTransactionManager.rollback(transaction);
}
}
AOP技术封装手动事务
@Component
@Aspect
public class TransactionAop {
@Autowired
private TransactionUtils transactionUtils;
@Around("execution(* com.kernel.service.UserService.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) {
try {
// 调用方法之前执行
System.out.println("开启事务");
TransactionStatus transactionStatus = transactionUtils.begin();
proceedingJoinPoint.proceed();
System.out.println("提交事务");
transactionUtils.commit(transactionStatus);
} catch (Throwable throwable) {
System.out.println("回滚事务");
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
}
事务注意事项:
一定不要将代码通过 try 包裹起来,如果程序发生异常,事务接收不到异常,就会认为程序正常执行,就不会进行回滚,必须手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
声明式事务
通过 AOP 实现,对方法进行拦截,在方法执行之前开启事务,结束后提交事务,发生异常回滚事务
自定义事务注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtTransaction {
}
事务实现
@Component
@Aspect
public class TransactionAop {
@Autowired
private TransactionUtils transactionUtils;
private TransactionStatus transactionStatus = null;
/**
* AOP实现事务管理
*
* @param proceedingJoinPoint 切面通知对象
*/
@Around("execution(* com.kernel.service.*.* (..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) {
try {
// 获取注解对象
ExtTransaction extTransaction = getExtTransaction(proceedingJoinPoint);
begin(extTransaction);
// 执行目标方法
proceedingJoinPoint.proceed();
// 提交事务
commit();
} catch (Throwable throwable) {
transactionUtils.rollback();
}
}
/**
* 获取注解对象
*
* @param proceedingJoinPoint 切面通知对象
* @return 注解对象
* @throws NoSuchMethodException
*/
public ExtTransaction getExtTransaction(ProceedingJoinPoint proceedingJoinPoint) throws NoSuchMethodException {
// 获取方法名称
String method = proceedingJoinPoint.getSignature().getName();
// 获取目标方法
Class<?> classTarget = proceedingJoinPoint.getTarget().getClass();
// 获取目标对象类型
Class[] parameterTypes = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterTypes();
// 获取目标对象方法
Method objMethod = classTarget.getMethod(method, parameterTypes);
// 获取注解
ExtTransaction declaredAnnotation = objMethod.getDeclaredAnnotation(ExtTransaction.class);
return declaredAnnotation;
}
/**
* 开启事务
* @param extTransaction 注解对象
* @return 事务对象
*/
TransactionStatus begin(ExtTransaction extTransaction) {
if (extTransaction != null)
transactionStatus = transactionUtils.begin();
return transactionStatus;
}
/**
* 提交事务
*/
void commit() {
if (transactionStatus != null)
transactionUtils.commit(transactionStatus);
}
/**
* 回滚事务
*/
void rollback() {
transactionUtils.rollback();
}
}
Spring事物传播行为
- PROPAGATION_REQUIRED:如果当前有事务,就用当前事务,如果当前没有事务,就新建一个事务
- PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
- PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常
- PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
什么是 Spring IOC?
Spring IOC 指的是控制反转,IOC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖,交由Spring来管理这些,实现解耦
手写 Spring IOC
实现步骤:
扫包
将标注了注解的类,通过反射创建实例并添加的 bean 容器中
当用户向容器要 bean 时,通过 beanId 在 bean 容器中查找并返回实例
package com.kernel.ext;
import com.kernel.ext.annotation.ExtAutoWired;
import com.kernel.ext.annotation.ExtService;
import com.kernel.utils.ClassUtil;
import org.apache.commons.lang.StringUtils;
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* IOC 注解版本
*/
public class ExtClassPathXmlApplicationContext {
// 包名
private String packageName;
// bean容器
private ConcurrentHashMap<String, Object> beans = null;
/**
* 构造函数
*
* @param packageName 包名
* @throws InstantiationException
* @throws IllegalAccessException
*/
public ExtClassPathXmlApplicationContext(String packageName) throws InstantiationException, IllegalAccessException {
this.packageName = packageName;
init();
}
/**
* 初始化对象
*
* @throws IllegalAccessException
* @throws InstantiationException
*/
private void init() throws IllegalAccessException, InstantiationException {
// 遍历所有类
List<Class<?>> classes = ClassUtil.getClasses(packageName);
// 将所有标注ExtService注解的类加入到容器中
findAnnotationByClasses(classes);
}
/**
* 过滤标注ExtService注解的类
*
* @param classes
* @throws InstantiationException
* @throws IllegalAccessException
*/
private void findAnnotationByClasses(List<Class<?>> classes) throws InstantiationException, IllegalAccessException {
for (Class classInfo : classes) {
ExtService extService = (ExtService) classInfo.getAnnotation(ExtService.class);
if (extService != null) {
Object newInstance = newInstance(classInfo);
beans.put(toLowerCaseFirstOne(classInfo.getSimpleName()), newInstance);
}
}
}
/**
* 通过反射构建对象
*
* @param classInfo
* @return
* @throws InstantiationException
* @throws IllegalAccessException
*/
private Object newInstance(Class classInfo) throws InstantiationException, IllegalAccessException {
return classInfo.getClass().newInstance();
}
/**
* 通过beanId查找对应的实例
*
* @param beanId
* @return
*/
public Object getBean(String beanId) throws IllegalAccessException {
Object object = null;
if (StringUtils.isEmpty(beanId))
return null;
for (String id : beans.keySet())
if (beanId.equals(id)) {
object = beans.get(beanId);
attrAssign(object);
break;
}
return object;
}
/**
* 依赖注入
*/
void attrAssign(Object object) throws IllegalAccessException {
Class<?> aClass = object.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
ExtAutoWired extAutoWired = field.getAnnotation(ExtAutoWired.class);
if (extAutoWired != null) {
field.setAccessible(true);
Object bean = getBean(field.getName());
field.set(field.getName(), object);
}
}
}
/**
* 首字母变小写
*
* @param s
* @return
*/
public static String toLowerCaseFirstOne(String s) {
if (Character.isLowerCase(s.charAt(0)))
return s;
else {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(Character.toLowerCase(s.charAt(0)));
stringBuffer.append(s.substring(1));
return stringBuffer.toString();
}
}
}
Spring MVC 原理
执行流程:
-
用户请求 url 至前端控制器 DispatcherServlet
-
DispatcherServlet 调用处理器映射器 HandlerMapping
-
HandlerMapping 根据 url 找到具体的处理器生成处理器执行链,并将执行链返回给 DispatcherServlet
-
DispatcherServlet 根据处理器 Handler 获取处理器适配器 HandlerAdapter 执行
-
执行 Handler
-
返回 ModelAndView 返回给 DispatcherServlet
-
DispatcherServlet 将 ModelAnd view 传递给视图解析器 ViewResolver
-
ViewResolver 解析成具体 View
-
渲染视图
- 响应页面给用户
Servlet 生命周期
init:在 Servlet 生命周期中,该方法仅执行一次,它是在将服务器装入 Servlet 时执行的,负责初始化 Servlet 对象,Servlet 是单例多线程的
service:负责响应请求,每当一个客户请求一个 HttpServlet 对象,该对象的 Service 方法就要被调用,传递一个 ServletRequest 和 ServletResponse 对象
destroy:在服务器停止卸载 Servlet 时调用
手写 Spring MVC
实现步骤:
创建一个 ExtDispatcherServlet 继承 HttpServlet
扫包
将标注了 @ExtController 注解的类,通过反射创建对象添加到容器中,将 beanId 和控制器关联
将标注了 @ExtRequestMapping 注解的类,将请求url 和控制器对象关联,将 url 和 方法关联
当用户请求 url 时,查找和 url 对应的对象,然后查找和 url 对应的方法,执行方法,解析并渲染
package com.kernel.ext.servlet;
import com.kernel.controller.ExtIndexController;
import com.kernel.ext.annotation.ExtController;
import com.kernel.ext.annotation.ExtRequestMapping;
import com.kernel.utils.ClassUtil;
import org.apache.commons.lang.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 手写SpringMVC
*/
public class ExtDispatcherServlet extends HttpServlet {
// 关联beanId和Object
private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<>();
// 关联url和控制器对象
private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<>();
// 关联url和methodName
private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<>();
/**
* 初始化Servlet
*/
public void init() {
try {
List<Class<?>> classes = ClassUtil.getClasses("com.kernel.controller");
findClassMVCBeans(classes);
handlerMapping(mvcBeans);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 关联url和控制器对象、url和methoName
* @param mvcBeans
*/
private void handlerMapping(ConcurrentHashMap<String, Object> mvcBeans) {
for (Object classInfo : mvcBeans.values()) {
ExtRequestMapping extCla***equestMapping = classInfo.getClass().getDeclaredAnnotation(ExtRequestMapping.class);
String requestBaseUrl = null;
if (extCla***equestMapping != null) {
requestBaseUrl = extCla***equestMapping.value();
}
Method[] methods = classInfo.getClass().getDeclaredMethods();
for (Method method : methods) {
ExtRequestMapping extMthodRequestMapping = method.getDeclaredAnnotation(ExtRequestMapping.class);
if (extCla***equestMapping != null){
String httpRequestUrl = extMthodRequestMapping.value();
mvcBeanUrl.put(requestBaseUrl + httpRequestUrl, classInfo);
mvcMethodUrl.put(requestBaseUrl + httpRequestUrl, method.getName());
}
}
}
}
/**
* 将所有控制器添加到mvcBeans中
* @param classes 包内所有类
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
private void findClassMVCBeans(List<Class<?>> classes) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
for (Class classInfo : classes) {
ExtController extController = (ExtController) classInfo.getDeclaredAnnotation(ExtController.class);
if (extController != null){
mvcBeans.put(classInfo.getName(), ClassUtil.newInstance(classInfo));
}
}
}
/**
* get请求
* @param req
* @param resp
* @throws IOException
* @throws ServletException
*/
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
try {
doPost(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* post请求
* @param req
* @param resp
*/
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
try {
doDispatch(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 路由分发
* @param req
* @param resp
* @throws Exception
*/
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String requestUrl = req.getServletPath();
Object object = mvcBeanUrl.get(requestUrl);
if (object == null)
object = ExtIndexController.class.newInstance();
String methodName = mvcMethodUrl.get(requestUrl);
if (StringUtils.isEmpty(methodName))
methodName = "error";
Class<?> classInfo = object.getClass();
String resultPage = (String) methodInvoke(classInfo, object, methodName);
viewDisplay(resultPage, req, resp);
}
/**
* 视图渲染
* @param resultPage
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
private void viewDisplay(String resultPage, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String suffix = ".jsp";
String prefix = "/";
req.getRequestDispatcher(prefix + resultPage + suffix).forward(req, resp);
}
/**
* 反射执行方法
* @param classInfo 控制器
* @param object 控制器对象
* @param methodName 方法名称
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
*/
private Object methodInvoke(Class<?> classInfo, Object object, String methodName) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
Method method = null;
try {
method = classInfo.getDeclaredMethod(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
finally {
return method.invoke(object);
}
}
}
标签:事务,classInfo,DI,Spring,param,MVC,private,import,throws 来源: https://blog.51cto.com/13559120/2388239