美文网首页程序员
万字长文!深入解析SpringAOP源码,从无到有分分钟搞定

万字长文!深入解析SpringAOP源码,从无到有分分钟搞定

作者: 996小迁 | 来源:发表于2021-01-20 17:57 被阅读0次

    一、认识AOP及其使用

    二、AOP的特点

    2.1 Spring AOP

    2.1.1 他是基于动态代理实现的

    Spring 提供了很多的实现AOP的方式:Spring 接口方式,schema配置方式和注解的方式. 
    如果使用接口方式引入AOP, 就是用JDK提供的动态代理来实现.
    如果没有使用接口的方式引入. 那么就是使用CGLIB来实现的
    

    研究使用接口方式实现AOP, 目的是为了更好地理解spring使用动态代理实现AOP的两种方

    2.1.2 Spring提供了对AspectJ的支持, 但只提供了部分功能的支持: 即AspectJ的切点解析(表达式)和匹配

    我们在写切面的时候,经常使用到的@Aspect, @Before, @Pointcut, @After, @AfterReturning, @AfterThrowing等就是AspectJ提供的.

    我们知道AspectJ很好用, 效率也很高. 那么为什么Spring不使用AspectJ全套的东西呢? 尤其是AspectJ的静态织入.

    先来看看AspectJ有哪些特点

    AspectJ的特点
    1. AspectJ属于静态织入. 他是通过修改代码实现的. 它的织入时机有三种
        1) Compile-time weaving: 编译期织入. 例如: 类A使用AspectJ增加了一个属性. 类B引用了类A, 这个场景就需要在编译期的时候进行织入, 否则类B就没有办法编译, 会报错.
        2) Post-compile weaving: 编译后织入.也就是已经生成了.class文件了, 或者是都已经达成jar包了. 这个时候, 如果我们需要增强, 就要使用到编译后织入
        3) Loading-time weaving: 指的是在加载类的时候进行织入. 
    
    2. AspectJ实现了对AOP变成完全的解决方案. 他提供了很多Spring AOP所不能实现的功能
    3. 由于AspectJ是在实际代码运行前就完成了织入, 因此可以认为他生成的类是没有额外运行开销的.
    

    扩展: 这里为什么没有使用到AspectJ的静态织入呢? 因为如果引入静态织入, 需要使用AspectJ自己的解析器. AspectJ文件是以aj后缀结尾的文件, 这个文件Spring是没有办法, 因此要使用AspectJ自己的解析器进行解析. 这样就增加了Spring的成本.

    三、 AOP的配置方式

    上面说了Spring AOP和AspectJ. 也说道了AspectJ定义了很多注解, 比如: @Aspect, @Pointcut, @Before, @After等等. 但是, 我们使用Spring AOP是使用纯java代码写的. 也就是说他完全属于Spring, 和AspectJ没有什么关系. Spring只是沿用了AspectJ中的概念. 包括AspectJ提供的jar包的注解. 但是, 并不依赖于AspectJ的功能.

    Spring AOP有三种配置方式.

    • 第一种: 基于接口方式的配置. 在Spring1.2版本, 提供的是完全基于接口方式实现的* 第二种: 基于schema-based配置. 在spring2.0以后使用了xml的方式来配置.* 第三种: 基于注解@Aspect的方式. 这种方式是最简单, 方便的. 这里虽然叫做AspectJ, 但实际上和AspectJ一点关系也没有.

    因为我们在平时工作中主要使用的是注解的方式配置AOP, 而注解的方式主要是基于第一种接口的方式实现的. 所以, 我们会重点研究第一种和第三种配置方式.

    3.1 基于接口方式的配置. 在Spring1.2版本, 提供的是完全基于接口方式实现的

    这种方式是最古老的方式, 但由于spring做了很好的向后兼容, 所以, 现在还是会有很多代码使用这种方式, 比如:声明式事务.

    那么, 在没有引入AspectJ的时候, Spring是如何实现AOP的呢? 我们来看一个例子:

    1.定义一个业务逻辑接口类

    package com.lxl.www.aop.interfaceAop;
    
    /**
     * 使用接口方式实现AOP, 默认通过JDK的动态代理来实现. 非接口方式, 使用的是cglib实现动态代理
     *
     * 业务接口类-- 计算器接口类
     *
     * 定义三个业务逻辑方法
     */
    public interface IBaseCalculate {
    
        int add(int numA, int numB);
    
        int sub(int numA, int numB);
    
        int div(int numA, int numB);
    
        int multi(int numA, int numB);
    
        int mod(int numA, int numB);
    
    }
    

    2.定义业务逻辑类

    package com.lxl.www.aop.interfaceAop;//业务类,也是目标对象
    
    import com.lxl.www.aop.Calculate;
    
    import org.springframework.aop.framework.AopContext;
    import org.springframework.stereotype.Service;
    
    /**
     * 业务实现类 -- 基础计算器
     */
    
    public class BaseCalculate implements IBaseCalculate {
    
        @Override
        public int add(int numA, int numB) {
            System.out.println("执行目标方法: add");
            return numA + numB;
        }
    
        @Override
        public int sub(int numA, int numB) {
            System.out.println("执行目标方法: sub");
            return numA - numB;
        }
    
        @Override
        public int multi(int numA, int numB) {
            System.out.println("执行目标方法: multi");
            return numA * numB;
        }
    
        @Override
        public int div(int numA, int numB) {
            System.out.println("执行目标方法: div");
            return numA / numB;
        }
    
        @Override
        public int mod(int numA, int numB) {
            System.out.println("执行目标方法: mod");
    
            int retVal = ((Calculate) AopContext.currentProxy()).add(numA, numB);
            return retVal % numA;
        }
    }
    

    3. 定义通知类

    前置通知

    package com.lxl.www.aop.interfaceAop;
    
    import org.springframework.aop.MethodBeforeAdvice;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 定义前置通知
     * 实现MethodBeforeAdvice接口
     */
    public class BaseBeforeAdvice implements MethodBeforeAdvice {
    
        /**
         *
         * @param method 切入的方法
         * @param args 切入方法的参数
         * @param target 目标对象
         * @throws Throwable
         */
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("===========进入beforeAdvice()============");
            System.out.println("前置通知--即将进入切入点方法");
            System.out.println("===========进入beforeAdvice() 结束============\n");
        }
    
    }
    
    

    后置通知

    package com.lxl.www.aop.interfaceAop;
    
    import org.aspectj.lang.annotation.AfterReturning;
    import org.springframework.aop.AfterAdvice;
    import org.springframework.aop.AfterReturningAdvice;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 后置通知
     * 实现AfterReturningAdvice接口
     */
    public class BaseAfterReturnAdvice implements AfterReturningAdvice {
    
        /**
         *
         * @param returnValue 切入点执行完方法的返回值,但不能修改
         * @param method 切入点方法
         * @param args 切入点方法的参数数组
         * @param target 目标对象
         * @throws Throwable
         */
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            System.out.println("\n==========进入afterReturning()===========");
            System.out.println("后置通知--切入点方法执行完成");
            System.out.println("==========进入afterReturning() 结束=========== ");
        }
    
    }
    
    

    环绕通知

    package com.lxl.www.aop.interfaceAop;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 环绕通知
     * 实现MethodInterceptor接口
     */
    public class BaseAroundAdvice implements MethodInterceptor {
    
        /**
         * invocation :连接点
         */
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("===========around环绕通知方法 开始===========");
            // 调用目标方法之前执行的动作
            System.out.println("环绕通知--调用方法之前: 执行");
            // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
            Object returnValue = invocation.proceed();
            System.out.println("环绕通知--调用方法之后: 执行");
            System.out.println("===========around环绕通知方法  结束===========");
            return returnValue;
        }
    
    }
    

    配置类

    package com.lxl.www.aop.interfaceAop;
    
    import org.springframework.aop.framework.ProxyFactoryBean;
    import org.springframework.context.annotation.Bean;
    
    /**
     * 配置类
     */
    public class MainConfig {
    
        /**
         * 被代理的对象
         * @return
         */
        @Bean
        public IBaseCalculate baseCalculate() {
            return new BaseCalculate();
        }
    
        /**
         * 前置通知
         * @return
         */
        @Bean
        public BaseBeforeAdvice baseBeforeAdvice() {
            return new BaseBeforeAdvice();
        }
    
        /**
         * 后置通知
         * @return
         */
        @Bean
        public BaseAfterReturnAdvice baseAfterReturnAdvice() {
            return new BaseAfterReturnAdvice();
        }
    
        /**
         * 环绕通知
         * @return
         */
        @Bean
        public BaseAroundAdvice baseAroundAdvice() {
            return new BaseAroundAdvice();
        }
    
        /**
         * 使用接口方式, 一次只能给一个类增强, 如果想给多个类增强, 需要定义多个ProxyFactoryBean
         * 而且, 曾增强类的粒度是到类级别的. 不能指定对某一个方法增强
         * @return
         */
        @Bean
        public ProxyFactoryBean calculateProxy() {
            ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
            proxyFactoryBean.setInterceptorNames("baseAfterReturnAdvice", "baseBeforeAdvice", "baseAroundAdvice");
            proxyFactoryBean.setTarget(baseCalculate());
            return proxyFactoryBean;
        }
    
    }
    
    

    之前说过, AOP是依赖ioc的, 必须将其注册为bean才能实现AOP功能

    方法入口

    package com.lxl.www.aop.interfaceAop;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class InterfaceMainClass{
    
        public static void main(String[] args) {
            ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
            IBaseCalculate calculate = context.getBean("calculateProxy", IBaseCalculate.class);
            System.out.println(calculate.getClass());
            calculate.add(1, 3);
        }
    
    }
    

    执行结果:

    ===========进入beforeAdvice()============
    前置通知--即将进入切入点方法
    ===========进入beforeAdvice() 结束============
    
    ===========around环绕通知方法 开始===========
    环绕通知--调用方法之前: 执行
    执行目标方法: add
    环绕通知--调用方法之后: 执行
    ===========around环绕通知方法  结束===========
    
    ==========进入afterReturning()===========
    后置通知--切入点方法执行完成
    ==========进入afterReturning() 结束=========== 
    

    通过观察, 我们发现, 执行的顺序是: 前置通知-->环绕通知的前置方法 --> 目标逻辑 --> 环绕通知的后置方法 --> 后置通知.

    那么到底是先执行前置通知, 还是先执行环绕通知的前置方法呢? 这取决于配置文件的配置顺序

    这里,我们将环绕通知放在最后面, 所以, 环绕通知在前置通知之后执行.

      @Bean
        public ProxyFactoryBean calculateProxy() {
            ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
            proxyFactoryBean.setInterceptorNames( "baseAfterReturnAdvice", "baseBeforeAdvice", "baseAroundAdvice");
            proxyFactoryBean.setTarget(baseCalculate());
            return proxyFactoryBean;
        }
    

    那么, 如果我们将环绕通知放在前置通知之前. 就会先执行环绕通知

      @Bean
        public ProxyFactoryBean calculateProxy() {
            ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
            proxyFactoryBean.setInterceptorNames("baseAroundAdvice", "baseAfterReturnAdvice", "baseBeforeAdvice");
            proxyFactoryBean.setTarget(baseCalculate());
            return proxyFactoryBean;
        }
    
    

    运行结果

    ===========around环绕通知方法 开始===========
    环绕通知--调用方法之前: 执行
    ===========进入beforeAdvice()============
    前置通知--即将进入切入点方法
    ===========进入beforeAdvice() 结束============
    
    执行目标方法: add
    
    ==========进入afterReturning()===========
    后置通知--切入点方法执行完成
    ==========进入afterReturning() 结束=========== 
    环绕通知--调用方法之后: 执行
    ===========around环绕通知方法  结束===========
    

    上面提到了责任链, 那么什么是责任链呢? 如下图所示:

    Spring AOP源码分析

    有一条流水线. 比如生产流水线. 里面有许多道工序. 完成工序1 ,才能进行工序2, 依此类推.

    结合上面的demo, 来看一个责任链调用的demo.

    上面我们定义了两个方法. 一个是前置通知BaseBeforeAdvice 实现了MethodBeforeAdvice, 另一个是环绕通知BaseAroundAdvice 实现了MethodInterceptor. 如果想把这两个通知放在一个链上. 那么他们必须实现相同的接口. 但是, 现在不同.

    我们知道环绕通知, 有两部分, 一部分是环绕通知的前置通知, 一部分是环绕通知的后置通知. 所以, 我们可以将前置通知看作是环绕通知的前置通知部分.

    package com.lxl.www.aop.interfaceAop.chainDemo;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.MethodBeforeAdvice;
    
    /**
     * 为什么 我们可以让MethodBeforeAdvice 前置通知继承自环绕通知的接口呢?
     * 主要原因是, 环绕通知的前半部分, 就是前置通知
     */
    public class BeforeAdviceInterceptor implements MethodInterceptor {
    
      // 前置通知
      MethodBeforeAdvice methodBeforeAdvice;
    
      public BeforeAdviceInterceptor(MethodBeforeAdvice methodBeforeAdvice) {
        this.methodBeforeAdvice = methodBeforeAdvice;
      }
    
      /**
       * 使用了环绕通知的前半部分. 就是一个前置通知
       * @param invocation the method invocation joinpoint
       * @return
       * @throws Throwable
       */
      @Override
      public Object invoke(MethodInvocation invocation) throws Throwable {
        methodBeforeAdvice.before(invocation.getMethod(), invocation.getArguments(), invocation.getClass());
        return invocation.proceed();
      }
    }
    
    

    这段代码包装了前置通知, 让其扩展为实现MethodInterceptor接口. 这是一个扩展接口的方法.

    接下来我们要创建一条链. 这条链就可以理解为流水线上各个工人. 每个工人处理一个工序. 为了能够统一调用. 所有的工人都要实现同一个接口. 责任链的定义如下:

        /**
         * 把一条链上的都初始化
         *
         * 有一条链, 这条链上都有一个父类接口 MethodInterceptor.
         * 也就是说, 链上都已一种类型的工人. 但每种工人的具体实现是不同的. 不同的工人做不同的工作
         *
         * 这里定义了一个责任链. 连上有两个工人. 一个是前置通知. 一个是环绕通知.
         * 前置通知和环绕通知实现的接口是不同的. 为了让他们能够在一条链上工作. 我们自定义了一个MethodBeforeAdviceInterceptor
         * 相当于为BaseBeforeAdvice()包装了一层MethodInterceptor
         */
    
        List<MethodInterceptor> list = new ArrayList<>();
        list.add(new BeforeAdviceInterceptor(new BaseBeforeAdvice()));
        list.add(new BaseAroundAdvice());
    

    这里定义了一个责任链. 连上有两个工人. 一个是前置通知. 一个是环绕通知.

    前置通知和环绕通知实现的接口是不同的. 为了让他们能够在一条链上工作. 我们自定义了一个MethodBeforeAdviceInterceptor相当于为BaseBeforAdvice()包装了一层MethodInterceptor接下来是责任链的调用.

    /**
       * 责任链调用
       */
      public static class MyMethodInvocation implements MethodInvocation {
    
        // 这是责任链
        protected List<MethodInterceptor> list;
        // 目标类
        protected final BaseCalculate target;
    
        public MyMethodInvocation(List<MethodInterceptor> list) {
          this.list = list;
          this.target = new BaseCalculate();
        }
    
        int i = 0;
    
        public Object proceed() throws Throwable {
          if (i == list.size()) {
            /**
             * 执行到责任链的最后一环, 执行目标方法
             */
            return target.add(2, 2);
          }
          MethodInterceptor interceptor = list.get(i);
          i++;
          /**
           * 执行责任链调用
           * 这个调用链第一环是: 包装后的前置通知
           * 调用链的第二环是: 环绕通知.
           * 都执行完以后, 执行目标方法.
           */
          return interceptor.invoke(this);
        }
    
        @Override
        public Object getThis() {
          return target;
        }
    
        @Override
        public AccessibleObject getStaticPart() {
          return null;
        }
    
        @Override
        public Method getMethod() {
          try {
            return target.getClass().getMethod("add", int.class, int.class);
          } catch (NoSuchMethodException e) {
            e.printStackTrace();
          }
          return null;
        }
    
        @Override
        public Object[] getArguments() {
          return new Object[0];
        }
      }
    
    }
    

    这里重点看 proceed () 方法. 我们循环获取了list责任链的通知, 然后执行invoke()方法

    Spring AOP源码分析

    proceed() 方法是一个链式循环. 刚开始i=0, list(0)是前置通知, 当调用到前置通知的时候, BeforeAdviceInterceptor.invoke()方法, 又调用了invocation.proceed()方法, 回到了MyMethodInvocation.proceed()方法.

    然后i=1, list(1)是环绕通知, 当调用环绕通知的时候, 又调用了invocation.proceed(); 有回到了MyMethodInvocation.proceed()方法.

    这是已经是list的最后一环了, 后面不会在调用invoke()方法了. 二是执行目标方法. 执行结束以后, 整个调用结束.

    这就是一个调用链.

    对于责任链有两点:

    1. 要有一个统一的调用, 也就是一个共同的抽象类.

    2. 使用循环或者递归, 完成责任链的调用

    总结:

    上面这种方式, 使用的是ProxyFactoryBean 代理bean工厂的方式. 他有两个限制:

    public class ProxyFactoryBean extends ProxyCreatorSupport
            implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
    ......
       @Override
        @Nullable
        public Object getObject() throws BeansException {
            /**
             * 初始化通知链: 将通知放入链中
             * 后面初始化的时候, 是通过责任链的方式调用这些通知链的的. 
             * 那么什么是责任链呢?
             */
            initializeAdvisorChain();
            if (isSingleton()) {
                /**
                 * 创建动态代理
                 */
                return getSingletonInstance();
            }
            else {
                if (this.targetName == null) {
                    logger.info("Using non-singleton proxies with singleton targets is often undesirable. " +
                            "Enable prototype proxies by setting the 'targetName' property.");
                }
                return newPrototypeInstance();
            }
        }
    ......
    }
    
    

    1. 一次只能给1个类增强, 如果给多个类增强就需要定义多个ProxyFactoryBean

    2. 增强的粒度只能到类级别上, 不能指定给某个方法增强.这样还是有一定的限制

    3.2 基于注解@Aspect的方式. 这种方式是最简单, 方便的. 这里虽然叫做AspectJ, 但实际上和AspectJ一点关系也没有.

    3.2.1 @Aspect切面的解析原理

    上面第一种方式详细研究了接口方式AOP的实现原理. 注解方式的AOP, 最后就是将@Aspect 切面类中的@Befor, @After等注解解析成Advisor. 带有@Before类会被解析成一个Advisor, 带有@After方法的类也会被解析成一个Advisor.....其他通知的方法也会被解析成Advisor 在Advisor中定义了增强的逻辑, 也就是@Befor和@After等的逻辑, 以及需要增强的方法, 比如div方法.

    下面来分析一下使用注解@Aspect , @Before, @After的实现原理. 上面已经说了, 就是将@Before, @After生成Advisor

    这里一共有三个部分.

    • 第一部分: 解析@Aspect下带有@Before等的通知方法, 将其解析为Advisor* 第二部分: 在createBean的时候, 创建动态代理* 第三部分: 调用. 调用的时候, 执行责任链, 循环里面所有的通知. 最后输出结果.

    下面我们按照这三个部分来分析.

    第一步: 解析@Aspect下带有@Before等的通知方法, 将其解析为Advisor. 如下图:

    Spring AOP源码分析

    第一步是在什么时候执行的呢?

    在createBean的时候, 会调用很多PostProcessor后置处理器, 在调用第一个后置处理器的时候执行.执行的流程大致是: 拿到所有的BeanDefinition,判断类上是不是带有@Aspect注解. 然后去带有@Aspect注解的方法中找@Before, @After, @AfterReturning, @AfterThrowing, 每一个通知都会生成一个Advisor

    第二步: 在createBean的时候, 创建动态代理

    Spring AOP源码分析

    createBean一共有三个阶段, 具体在哪一个阶段创建的动态代理呢?

    整体流程是:

    在createBean的时候, 在初始化完成以后调用bean的后置处理器. 拿到所有的Advisor, 循环遍历Advisor, 然后根据execution中的表达式进行matchs匹配. 和当前创建的这个bean进行匹配, 匹配上了, 就创建动态代理.

    pointcut的种类有很多. 上面代码提到过的有:

    package com.lxl.www.aop.interfaceAop.chainDemo;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.MethodBeforeAdvice;
    
    /**
     * 为什么 我们可以让MethodBeforeAdvice 前置通知继承自环绕通知的接口呢?
     * 主要原因是, 环绕通知的前半部分, 就是前置通知
     */
    public class BeforeAdviceInterceptor implements MethodInterceptor {
    
      // 前置通知
      MethodBeforeAdvice methodBeforeAdvice;
    
      public BeforeAdviceInterceptor(MethodBeforeAdvice methodBeforeAdvice) {
        this.methodBeforeAdvice = methodBeforeAdvice;
      }
    
      /**
       * 使用了环绕通知的前半部分. 就是一个前置通知
       * @param invocation the method invocation joinpoint
       * @return
       * @throws Throwable
       */
      @Override
      public Object invoke(MethodInvocation invocation) throws Throwable {
        methodBeforeAdvice.before(invocation.getMethod(), invocation.getArguments(), invocation.getClass());
        return invocation.proceed();
      }
    }
    
    

    而我们注解里面是按照execution表达式的方式进行匹配的

    第三步: 调用. 调用的时候, 执行责任链, 循环里面所有的通知. 最后输出结果.

    Spring AOP源码分析

    3.2.2 AOP切面源码分析

    源码分析也分为三部分

    • 1. 解析切面
    • 2. 创建动态代理
    • 3. 调用
        /**
         * 把一条链上的都初始化
         *
         * 有一条链, 这条链上都有一个父类接口 MethodInterceptor.
         * 也就是说, 链上都已一种类型的工人. 但每种工人的具体实现是不同的. 不同的工人做不同的工作
         *
         * 这里定义了一个责任链. 连上有两个工人. 一个是前置通知. 一个是环绕通知.
         * 前置通知和环绕通知实现的接口是不同的. 为了让他们能够在一条链上工作. 我们自定义了一个MethodBeforeAdviceInterceptor
         * 相当于为BaseBeforeAdvice()包装了一层MethodInterceptor
         */
    
        List<MethodInterceptor> list = new ArrayList<>();
        list.add(new BeforeAdviceInterceptor(new BaseBeforeAdvice()));
        list.add(new BaseAroundAdvice());
    
    

    源码分析的入口, AOP注解:

    package com.lxl.www.aop;
    
    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @Configurable
    // 使用注解的方式引入AOP
    @EnableAspectJAutoProxy
    @ComponentScan("com.lxl.www.aop")
    public class MainConfig {
    
    }
    

    引入AOP, 我们需要在配置文件中增加@EnableAspectJAutoProxy代理. 那么想要去掉AOP的引入, 只需要将这个注解注释掉就可以了. 这个注解解释整个AOP的入口.

    接下来, 进入到注解类

    /**
       * 责任链调用
       */
      public static class MyMethodInvocation implements MethodInvocation {
    
        // 这是责任链
        protected List<MethodInterceptor> list;
        // 目标类
        protected final BaseCalculate target;
    
        public MyMethodInvocation(List<MethodInterceptor> list) {
          this.list = list;
          this.target = new BaseCalculate();
        }
    
        int i = 0;
    
        public Object proceed() throws Throwable {
          if (i == list.size()) {
            /**
             * 执行到责任链的最后一环, 执行目标方法
             */
            return target.add(2, 2);
          }
          MethodInterceptor interceptor = list.get(i);
          i++;
          /**
           * 执行责任链调用
           * 这个调用链第一环是: 包装后的前置通知
           * 调用链的第二环是: 环绕通知.
           * 都执行完以后, 执行目标方法.
           */
          return interceptor.invoke(this);
        }
    
        @Override
        public Object getThis() {
          return target;
        }
    
        @Override
        public AccessibleObject getStaticPart() {
          return null;
        }
    
        @Override
        public Method getMethod() {
          try {
            return target.getClass().getMethod("add", int.class, int.class);
          } catch (NoSuchMethodException e) {
            e.printStackTrace();
          }
          return null;
        }
    
        @Override
        public Object[] getArguments() {
          return new Object[0];
        }
      }
    
    }
    
    

    这是, 我们看到EnableAspectJAutoProxy类增加了一个@Import注解类, 我们知道Import注解可以向IoC容器中增加一个bean.

    下面进入到 AspectJAutoProxyRegistrar类

    package org.springframework.context.annotation;
    
    import org.springframework.aop.config.AopConfigUtils;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.core.annotation.AnnotationAttributes;
    import org.springframework.core.type.AnnotationMetadata;
    
    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
        /**
         * Register, escalate, and configure the AspectJ auto proxy creator based on the value
         * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
         * {@code @Configuration} class.
         */
        @Override
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
            AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    
            AnnotationAttributes enableAspectJAutoProxy =
                    AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
            if (enableAspectJAutoProxy != null) {
                if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
                if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
                }
            }
        }
    
    }
    
    

    我们看到, 使用ImportBeanDefinitionRegistrar注册了一个BeanDefinition.

    需要记住的是, 通常使用ImportBeanDefinitionRegistrar结合@Import可以向容器中注册一个BeanDefinition.

    如何注册的呢? 看具体实现.

    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    

    注册名字是internalAutoProxyCreator的AnnotationAwareAspectJAutoProxyCreator

    @Nullable
        public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                BeanDefinitionRegistry registry, @Nullable Object source) {
    
            /**
             * 注册一个AnnotationAwareAspectJAutoProxyCreator类型的bean定义
             */
            return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
        }
    

    如上结构梳理如下:

    Spring AOP源码分析

    我们看到, 注册了类AnnotationAwareAspectJAutoProxyCreator类型的bean. 这是一个什么样的类呢? 我们来看一下类的结构. 这个类的继承结构很庞大, 我们只看和本次内容相关的继承结构

    Spring AOP源码分析

    解析切面, 创建动态代理, 都是在bean的后置处理器中进行的, 下面对照着AOP的实现原理以及createBean(创建bean)的过程来看

    Spring AOP源码分析

    上图是bean加载过程中调用的9次后置处理器. 在创建bean之前调用了InstantiationAwareBeanPostProcessor后置处理器判断是否需要为这个类创建AOP, 也就是解析切面的过程. 所以在AnnotationAwareAspectJAutoProxyCreator里面实现了InstantiationAwareBeanPostProcessor后置处理器的接口. 重写了postProcessBeforeInstantiation方法.

    在createBean的第三阶段初始化之后, 要创建AOP的动态代理, 调用了BeanPostProcess后置处理器, AnnotationAwareAspectJAutoProxyCreator也实现了BeanPostProcess接口. 重写了postProcessAfterInitialization.

    同时也需要处理AOP的循环依赖的问题, 处理循环依赖是在属性赋值之前调用SmartInstantiationAwareBeanPostProcessor后置处理器, 然后重写getEarlyBeanReference方法. 我们看到AnnotationAwareAspectJAutoProxyCreator也实现了SmartInstantiationAwareBeanPostProcessor接口. 并重写getEarlyBeanReference方法.

    由于篇幅限制的原因,就只能分享一部分内容,需要完整版的小伙伴可以帮忙转发+关注,感谢大家!

    相关文章

      网友评论

        本文标题:万字长文!深入解析SpringAOP源码,从无到有分分钟搞定

        本文链接:https://www.haomeiwen.com/subject/aqokzktx.html