手写 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 原理

执行流程:

  1. 用户请求 url 至前端控制器 DispatcherServlet
  2. DispatcherServlet 调用处理器映射器 HandlerMapping
  3. HandlerMapping 根据 url 找到具体的处理器生成处理器执行链,并将执行链返回给 DispatcherServlet
  4. DispatcherServlet 根据处理器 Handler 获取处理器适配器 HandlerAdapter 执行
  5. 执行 Handler
  6. 返回 ModelAndView 返回给 DispatcherServlet
  7. DispatcherServlet 将 ModelAnd view 传递给视图解析器 ViewResolver
  8. ViewResolver 解析成具体 View
  9. 渲染视图
  10. 响应页面给用户

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);

        }
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券