首页 > 编程语言 >spring源码学习(九)springAOP实现过程

spring源码学习(九)springAOP实现过程

时间:2022-11-04 22:36:38浏览次数:41  
标签:拦截器 对象 spring 代理 源码 切面 创建 springAOP 执行

一、aop中所需要的beanDefinition对象的解析

  

 

 

   当xml文件解析到<aop:config>这个节点时,就会解析aop所需要的所有对象

  1、在解析到<aop:config>节点时,会向ioc容器中注册内置的beanDefinition:org.springframework.aop.config.internalAutoProxyCreator 所对应的对象即AspectJAwareAdvisorAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator是一个BPP为后期创建aop所需代理对象

 

  2、解析pointcut节点,创建并注册beanDefinition:AspectJExpressionPointcut

  3、解析aspect节点

    1)、判断子节点是否是adviceNode类型,如果不是直接跳过,如果是则继续解析

      adviceNode类型即:before、after、after-returning、after-throwing、around五种类型

 

 

     2)、解析具体子节点,解析完成之后beanDefinition的嵌套情况如下图所示

 

 

 

 

 到此,aop所需的全部beanDefinition解析完毕

 

二、对象的创建

   1、切面对象的创建

    在执行到finishBeanFactoryInitialization(beanFactory)(实例化所有非懒加载的单例bean)这个方法时,创建第一个单例bean的时候。在本案例中一个需要被创建的bean是user对象如下图所示

    

 

    在user对象创建的过程中会执行到一个方法Object bean = resolveBeforeInstantiation(beanName, mbdToUse),这个方法也是我们前面所说到的bean的创建方式的一种通过BPP代理创建对象,

    这里的BPP也就是在创建之前注入到容器中的代理对象创建类org.springframework.aop.config.internalAutoProxyCreator。

    在执行AbstractAutoProxyCreator的前置增强方法时,会判断容器中是否存在需要创建的切面对象,如果有并且切面对象还未创建,那么就去创建这些切面对象

    如下图所示调用流程

      1)、在shouldSikp方法中获取所有的切面

      2)、在获取切面的方法中会先拿到这些切面的beanName集合

      3)、再遍历beanName集合,判断容器中是否存在该切面对象,如果存在则跳过,如果不存在则执行创建bean的流程,并且加入到advisors切面集合中;最后把该集合返回。

                 

 

    ---shouldSkip这个方法的目的在于:

      判断当前需要创建的bean是否属于一个切面,如果是一个切面则需要跳过;如果不是一个切面,那么执行后续逻辑。

      后续逻辑是否执行自定义代理源,进行代理对象的创建。(在本案例中没有自定义代理源,所以后续逻辑直接跳过)

  2、代理对象的创建

    在前面学习spring循环依赖的博客(https://www.cnblogs.com/banzhuandang/p/16192621.html)有说到,如果该对象需要被代理,spring的设计原则是在该bean初始化完成才会创建代理对象,在本案例中没有循环依赖问题,所以代理的创建就在user对象初始化完成之后,即在执行完initMethods方法,执行bpp的after方法中去创建代理对象

    

 

   代理对象创建的核心方法wrapIfNecessary

    1)、先执行一系列的判断逻辑,是否需要创建代理对象

       

 

    2)、拿到所有的切面,并且加入一个ExposeInvocationInterceptor对象(这个对象的作用后续会说明),

      

 

    3)、执行具体的代理创建逻辑createProxy();

       通过代理工厂拿到具体的代理创建方式,然后去创建代理。代理对象的具体创建这里不再熬述

 

      

      代理创建的两种方式,如下

                      

 

 

 三、代理对象方法执行流程

   我们可以System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,path)(cglib的方式);设置生成的代理对象文件持久化到本地

   可以看到我当前的案例已经把生成的代理对象class文件保存到本地了,我们执行的方法实际上就是代理对象中的方法。

 

   代理对象中的show方法:至于这个属性CGLIB$CALLBACK_0是怎么设置的,可以详细去研究cglib创建代理对象的过程;

  我们可以看到执行的show方法实际的入口是从MethodInterceptor.intercept()开始的,下面我们详细去研究这个方法

    

 

   1、获取执行链路

    执行链路chain,该链路是在代理对象创建的时候已经生成好了,在前文对象的创建已经解释过。

    拦截器链的顺序与advisor的顺序一致,而advisors的顺序是在其创建时候已经排序完成的,同一个的aspect下的advisor的顺序是根据解析的顺利一致,而不同aspect下的advisor的顺利与Order指定的优先级有关,数字越低优先级越高。

    

 

 

    那么chain到底是如何执行的呢?,

    

 

     把拦截器链交给CglibMethodInvocation来执行,CglibMethodInvocation实现了ReflectiveMethodInvocation,通过反射的方式去执行拦截器链中的所有拦截器

    

  2、拦截器链的执行

   

   第一个拦截器ExposeInvocationInterceptor,获取通知链的时候会将其放到通知链首位,用于暴露方法调用放到ThreadLocal中,方便后续拦截器中的拦截器获取

    

 

   获取方法

  

 

   整个拦截器的执行过程

 

执行结果

  

  有一点需要注意的地方,异常拦截器尽量配置在拦截器的第一个位置,如本案例中配置在拦截器的最后位置,那么如果在执行到异常拦截器之前执行的通知出现异常了就无法捕获到异常,并执行异常通知

   例如我们在前置通知中自定义一个异常

  

 

   此时异常通知的配置在最后一位

 

   执行结果:

 

   异常通知配置在第一位:

  

  执行结果:

 

   

 

   因为异常拦截器的try-catch的范围并没有包含前面的拦截器

 

     

 

 

 

  

标签:拦截器,对象,spring,代理,源码,切面,创建,springAOP,执行
From: https://www.cnblogs.com/banzhuandang/p/16590138.html

相关文章