手工實現AOP核心源碼之aop1.0版

目錄

  1. 環境準備

  2. 核心代碼實現

  3. 測試

  4. 總結

環境準備

  1. spring源碼版本5.0.2

  2. jdk 1.8

  3. ide  idea

  4. 在源碼中創建子模塊

     

友情提示: 

            我們1.0版本只需要實現前置,後置通知即可。由於ioc源碼需要依賴spring 本身的,所以spring ioc源碼有些地方需要改造下,實現方式上可能稍微有些不同,不過核心思想是一致的。後續手寫ioc的時候將這塊整合到一起去

核心代碼實現

實現思路:

  1. 實現自己的註解  @Aspect   @Before  @After  @EnableAopAspect 

  2. 編寫AopBeanPostProcessor 用於增強邏輯,將原生對象轉換成代理對象返回給spring ioc

  3. 編寫AopAnntationRegisterBeanDefinition用於向spring添加aop的後置處理器的前期工作

  4. 編寫代理proxyFactory工廠,用於jdk和cglib不同的代理方式

  5. 編寫MethodInterceptor對切面進行封裝和執行

  6. 編寫Advice用於抽象各種通知

  7. 改造spring源碼context模塊註冊後置處理部分,我們自己的aop後置處理器由自己去實例化,即此PostProcessorRegistrationDelegate類registerBeanPostProcessors方法的改造

  • 思路有了我們來做具體實現吧

         在spring-context模塊中新建自己的包結構如圖:

  • 來看下我們自己的@EnableAopAspect註解:

  • bd註冊類和spring實現相同,沒什麼可看的,我們重點看下自己的aop後置處理器的具體實現邏輯:

public class AopBeanPostProcessor  implements BeanPostProcessor {

  private DefaultListableBeanFactory beanFactory;

  public AopBeanPostProcessor(ConfigurableListableBeanFactory beanFactory){
    this.beanFactory=(DefaultListableBeanFactory)beanFactory;
  }

  //重寫postProcessAfterInitialization 在原生對象填充完屬性執行完初始化方法後執行
  //對原生對象進行轉換操作
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    //從beanDefinitionMap中通獲取加了Aspect註解的的beanName
    String[] beanNamesForAnnotation = beanFactory.getBeanNamesForAnnotation(Aspect.class);
    //如果存在切面類,就進行轉換,否則直接返回原生對象
    if(beanNamesForAnnotation.length>0) {
      //獲取切面類,此工廠是我們通過構造方法傳入的,我們基於spring ioc實現的aop
      //所以有些地方需要改造.將DefaultListableBeanFactory傳入到自己的定義的後置處理器重用於獲取我們的切面類對象
      Object target = beanFactory.getBean(beanNamesForAnnotation[0]);
      Method[] declaredMethods = target.getClass().getDeclaredMethods();
      //通過反射,循環切面類中的所有方法
      for(Method method:declaredMethods){
        //如果該方法上存在Before註解,獲取其註解中的切面表達式,並判斷當前原生對象是否需要進行增強
        //即是否符合我們切面表達式
        if(method.isAnnotationPresent(Before.class)) {
          Before annotation = method.getAnnotation(Before.class);
          String name = bean.getClass().getName();
          String substring = annotation.value().substring(0, annotation.value().indexOf("("));
          Pattern pattern = Pattern.compile(substring);
          //如果符合切面表達式,將其轉換爲代理對象後返回給spring
          if (pattern.matcher(name).matches()) {
            MethodInterceptor methodInterceptor = new MethodInterceptor(method,
                target, method.getParameters());
            //3 實例化Advice
            Advice advice = new MethodBeforeAdvice(methodInterceptor);
            //將原生對象轉換爲代理對象
            return ProxyFactory.getProxy(bean, advice).getProxy();
          }
        }
      }
      return bean;
    }else {
      return bean;
    }
  }

}
  • 此處有個地方和spring不大一樣,就是增加了構造方法,用於傳遞DefaultListableBeanFactory 從而來獲取我們的切面類對象.還需改造的地方就是spring 註冊後置處理時的源碼,由於我們需要通過構造方法傳遞DefaultListableBeanFactory ,所以不能交由spring原生代碼去實例化,所以我們增加如下代碼:

 

:上面都加了註釋大家可以仔細看下後置處理器中的邏輯.

  • 代理工廠我們實現的比較簡單,不解析許多了,看下代碼吧

  • 我們的前置處理器中邏輯如下:

幾處的核心代碼基本都編寫完了,下面我們來測試一把

測試

1,在Configuration類中引入自己的@EnableAopAspect註解即開啓aop代理增強功能.

2,創建aop切面類,編寫前置通知,以及橫切邏輯的表達式

3,在main函數中啓動spring容器,獲取twoService對象實例.執行其query方法.

4,在切面類的前置通知方法處,打debug斷點,執行看結果

執行完後,再看控制檯結果

哎呦臥槽嘞,完美!!!

總結

至此我們1.0v aop實現完成,1.0v版本粗略實現了aop增強功能,並且支持jdk的動態代理,由於沒有手工實現ioc部分,所以有些地方和spring實現方式不同,而且環繞通知並未實現,但是核心原理是一樣的,我們後續在對1.0v進行改造升級2.0v,敬請期待。

 

個人公衆號:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章