手写 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@Aspectpublic 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@Aspectpublic 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 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> classes = ClassUtil.getClasses(packageName); // 将所有标注ExtService注解的类加入到容器中 findAnnotationByClasses(classes); } /** * 过滤标注ExtService注解的类 * * @param classes * @throws InstantiationException * @throws IllegalAccessException */ private void findAnnotationByClasses(List> 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 mvcBeans = new ConcurrentHashMap<>(); // 关联url和控制器对象 private ConcurrentHashMap mvcBeanUrl = new ConcurrentHashMap<>(); // 关联url和methodName private ConcurrentHashMap mvcMethodUrl = new ConcurrentHashMap<>(); /** * 初始化Servlet */ public void init() { try { List> classes = ClassUtil.getClasses("com.kernel.controller"); findClassMVCBeans(classes); handlerMapping(mvcBeans); } catch (Exception e) { e.printStackTrace(); } } /** * 关联url和控制器对象、url和methoName * @param mvcBeans */ private void handlerMapping(ConcurrentHashMap 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> 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); } }}