乐于分享
好东西不私藏

Spring——AOP之aop标签源码探查

Spring——AOP之aop标签源码探查

    每天进步一点点,大家好,我是大龄码农。

    在AOP之bean标签源码探查中主要讲解了“使用bean标签+Spring原生类”这种方式的源码解读,今天我们来聊聊引入aop标签后,Spring是如何处理的。

    以下说明主要分为三部分:

  • 第一部分:容器的启动
  • 第二部分:获取bean
  • 第三部分:执行目标方法
注:这里先以aop标签为例说明
第一部分 容器的启动

一、xml文件读取

    以前在讲IoC之源码初探时,只是简单介绍了读取xml文件并转换成BeanDefinition对象,这里稍微展开一些

    还记得XmlBeanDefinitionReader类的loadBeanDefinitions()方法吗?从这里一直找下去,就来到了这里

    说明:经典的XXX()方法中,包含doXXX()方法

    说明:这个方法就是要将xml中的元素转换成BeanDefinition对象了

    说明1:这里分为两大部分处理,一部分是默认处理的部分,另一部分是自定义处理的部分。

    说明2:平时使用的<import>、<bean>标签等都属于默认元素,而引入的aop标签就属于自定义的元素。

    说明:通过在xml中引入的命名空间,寻找对应的操作类

    说明:利用反射创建操作类的实例并初始化,在本例中,对应的子类是AopNamespaceHandler

    说明:针对不同的标签(aop冒号后面的部分),注册对应的解析类,并在下一步解析时使用

    说明:主要关注一下configureAutoProxyCreator()方法

    说明:一直走到AopConfigUtils#registerOrEscalateApcAsRequired()方法,手工注册一个BeanDefinition对象

    说明:这就是后面实例化时在缓存中使用的key,

    关于后面对于其他标签的解析类似,此处就不具体看了,最后的结果就是:

  • config对应AspectJAwareAdvisorAutoProxyCreator类

  • pointcut对应AspectJExpressionPointcut类

  • advisor对应DefaultBeanFactoryPointcutAdvisor类

    同时它们会被注册到一个复合组件的List中nestedComponents

    关于advisor在缓存中的key,因为是DefaultBeanFactoryPointcutAdvisor类,同时容器中可能存在多个advisor,所以需要有所区分,即DefaultBeanFactoryPointcutAdvisor#序号,具体逻辑可以参考BeanDefinitionReaderUtils#generateBeanName()方法

二、创建容器对象

    这里主要介绍代理类的处理过程,LogInCheck代理类对BankImpl类中的方法实现了增强效果。按照流程顺序,这里只重点说几个方法:

1、resolveBeforeInstantiation()

    该方法在AbstractAutowireCapableBeanFactory类中,目的是返回可能存在代理类,来代替对应的目标类

    说明:只有当bean是代理类时,才会进入画红框架的第二个判断

    说明:循环所有的BeanPostProcessors去处理,这里说一下AbstractAutoProxyCreator#postProcessBeforeInstantiation()

    说明:分两部分,上面部分是标识无需使用代理,存储在advisedBeans中,下面部分就是创建并返回具体的代理实例,对于id=”bank”,就会执行这个流程,但是本例中targetSource是null,所以也是返回null

 2、applyBeanPostProcessorsAfterInitialization()

    在AbstractAutowireCapableBeanFactory#initializeBean()中的一个方法

    说明:循环所有的BeanPostProcessors去处理,这里说一下AbstractAutoProxyCreator#postProcessAfterInitialization()方法,其中,主要是wrapIfNecessary()方法

    说明:分两部分,上面部分是判断,对于不需要使用代理的对象返回它本身,下面部分是判断是否存在符合条件的拦截器,如果有就创建并返回具体的代理实例。

      上面部分已经提前在postProcessBeforeInstantiation()做过处理了,所以只要取出来用就可以。下面部分中,当id=”bank”时,此时shouldSkip()方法是调用的子类AspectJAwareAdvisorAutoProxyCreator的

    说明:此处返回的是pointcut对应的实例列表,但是由于不是AspectJPointcutAdvisor的实例,所以最后还会调用父类的方法

    说明:条件都不满足,调用getAdvicesAndAdvisorsForBean()方法

    说明:第一个方法是找到所有的候选者,第二个方法是在所有的候选者中找到可以应用的,即能匹配到配置的expression表达式的,第三个是扩展一个Advisor

    说明:使用ExposeInvocationInterceptor对外暴露,后面还有排序的操作

    好了,回到wrapIfNecessary()方法,这时需要创建代理对象

    说明:这里使用ProxyFactory类来处理

    说明:在AOP之源码初探中使用的是DefaultAopProxyFactory

    说明:这里还是创建代理,依然是Jdk的代理

    回到wrapIfNecessary()方法创建代理对象的调用处

    说明1:此时将”bank”放入了advisedBeans中,且值为TRUE

    说明2:此时返回了代理对象proxy,那么initializeBean()方法返回的就是代理对象

    说明:此时bean存储的是目标类对象BankImpl,而exposedObject存储的是目标类的代理对象,那么什么时候暴露的呢?

    说明:此时对于id=”bank”的beanName来说,放入一级缓存的是代理对象,而从三级缓存中去除的是目标对象,这样就完成了暴露过程。

第二部分 获取bean

    从一级缓存中获取id=”bank”对应的bean,此时获取的是代理对象。

    注:此时在一级缓存中关于上面xml解析的对象只有DefaultBeanFactoryPointcutAdvisor对象,它的属性pointcut的对象是AspectJExpressionPointcut,属性adviceBeanName的值是logInCheck

第三部分 执行目标方法

    当调用目标类方法时,依然执行JdkDynamicAopProxy#invoke()方法,我们直接来到ReflectiveMethodInvocation#proceed()的方法,当然第一个执行的是ExposeInvocationInterceptor#invoke()方法

    说明:知道为什么要增加这个类,而且把它放在第一个了吗?对,线程安全,毕竟可能会存在多个拦截的方法。


趁热打铁,自定义切面的处理与上面类似,我就简单地说一下不同的地方

第一部分 容器的启动

    使用ConfigBeanDefinitionParser#parseAdvice()方法处理aspect配置,advisor对应AspectJPointcutAdvisor类,在BeanDefinition中存入constructorArgumentValues属性中的几个类:

  • 第1个是MethodLocatingFactoryBean
    说明:这是一个FactoryBean,会返回一个Method对象,也就是

MyCustomAspect#beforeTransfer
  • 第2个是pointcut,对应AspectJMethodBeforeAdvice类,在createAdviceDefinition()方法中实现

  • 第3个是SimpleBeanFactoryAwareAspectInstanceFactory类

在转换BeanDefinition时,名字也是进行了处理(AspectJPointcutAdvisor#序号)

    其他部分依然一样,即:

  • config对应AspectJAwareAdvisorAutoProxyCreator类

  • pointcut对应AspectJExpressionPointcut类

  • advisor对应DefaultBeanFactoryPointcutAdvisor类

    第二部分 获取bean

    从一级缓存中获取id=”bank”对应的bean,此时获取的是代理对象。

    注:此时在一级缓存中关于上面xml解析的对象除了DefaultBeanFactoryPointcutAdvisor对象,还有AspectJPointcutAdvisor对象,它的属性advice的对象是AspectJMethodBeforeAdvice,属性pointcut对应的是ComposablePointcut(classFilter属性是AspectJExpressionPointcut,因为有共同的父接口)

第三部分 执行目标方法

    我们直接来到调用ReflectiveMethodInvocation#proceed()的方法,此时有三个拦截方法,依次为:

    1、ExposeInvocationInterceptor

        这个就不多说了

    2、MethodBeforeAdviceInterceptor

        属性advice是AspectJMethodBeforeAdvice对象,它要调用的是MyCustomAspect#beforeTransfer()方法

    3、MethodBeforeAdviceInterceptor

        这个调用的是LogInCheck#before()方法

    所以,使用自定义切面时,唯一不同的是又多一层切面而已。当然,对于这个新增的切面,就能体现出Spring在处理过程还是有一些小细节的,比如:

  • shouldSkip()方法返回的是true

  • 拦截器的排序

特点

    生成的代理对象覆盖了原有的目标对象

    总结:
    1、介绍了aop标签的标准实现的源码
    2、介绍了aop标签+自定义切面实现的源码
本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » Spring——AOP之aop标签源码探查

猜你喜欢

  • 暂无文章