手寫 Spring 事務、IOC、DI 和 MVC

Spring AOP 原理

什麼是 AOP?前端

AOP 即面向切面編程,利用 AOP 能夠對業務進行解耦,提升重用性,提升開發效率java

應用場景:日誌記錄,性能統計,安全控制,事務處理,異常處理apache

AOP 底層實現原理是採用代理實現的編程

Spring 事務

基本特性:安全

  • 原子性
  • 隔離性
  • 一致性
  • 持久性

事務控制分類:性能優化

編程式事務:手動控制事務操做bash

聲明式事務:經過 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 原理

手寫 Spring 事務、IOC、DI 和 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);

        }
    }
}複製代碼

讀者福利

給你們推薦一個架構技術交流羣:714827309 ,裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析 ,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源,相信對於已經工做 和遇到技術瓶頸的碼友,在這個羣裏會有你須要的內容。 點擊連接加入羣聊【JAVA高級架構技術交流】:jq.qq.com/?_wv=1027&a…


相關文章
相關標籤/搜索