Reference
前言
本文作者试图从 Spring AOP 的执行流程上,通过源码分析的角度去弄清楚 Spring 容器内部是如何通过@AspectJ
通过使用接口的方式来实现 AOP 的;
本文为作者的原创作品,转载需注明出处;
源码分析
测试用例
继续沿用Spring Core Container 源码分析六:@Service 中使用的测试用例,为了能够测试 Spring AOP 的特性,这里需要做如下的改动,
- 为 DogService 添加接口;
增加接口 DogService.java,实现类 DogServiceImpl.java;目的是实现基于接口的 AOP 实现方式; - 为 DogService 的方法添加 AOP,DogServiceAspect.java;
DogService.java
1 | package org.shangyang.spring.container; |
DogServiceImpl.java
1 | package org.shangyang.spring.container; |
DogServiceAspect.java
1 | package org.shangyang.spring.container; |
当 Spring 容器解析到带有注解 @Aspect 的 Spring bean DogServiceAspect 的时候,与其 pointcut expression 相匹配的 beans 会通过 auto-proxing 技术自动的为其生成代理对象,并注入 Spring 容器中;
beans.xml
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
注意,要启动 @Aspectj auto-proxing 的 AOP 的方式,需要使用到 <aop:aspectj-autoproxy>
执行测试
1 |
|
1 | before around aspect ~~ |
可见,通过 AOP 在方法 DogService#walkDog(Dog) 的方法调用前后成功的织入了 DogServiceAspect#aroundWalkService 中的逻辑;
Advice、Advisor 和 Advised
有关 Advice、Advisor 和 Advised 的讲解参看【12.8】 Manipulating advised objects,在该章节的第二个有旗标处,作者做了一个总结和分类;总体而言,
- Advice
Advice 提供了对 join point 的 before、after 和 around 等的方法调用实现 - Advisor
在 Advice 上更进一步,Advice 在 join point 上定义了行为,而 Advisor 在此基础上通过 pointcut 定义了该行为怎么发生,什么时候发生 - Advised
Advised 接口,从命名上就非常清晰的体现了它的作用,已经被 Advice 之后的对象,叫做 Advised;从类的注解上可以清晰的看到,该接口对象保存了被代理对象(proxied object)的相关配置属性等
流程分析
分析思路及总纲
要弄清楚基于 @AspectJ 的 Spring AOP 的代理实现,需要从四个方面入手,
bean definition <aop:aspectj-autoproxy/> 是如何被解析的?
该部分参考 aop:aspectj-autoproxy element 章节;其核心作用就是去注册AnnotationAwareAspectJAutoProxyCreator
相关联的 post-bean-process definition@Aspect DogServiceAspect (AspectJ-annoated bean) 是如何被解析成 Advisor 并被注入 Spring 容器中的?
该部分参考解析并注册 @Aspect DogServiceAspect 到 Spring 容器小节;DogService 是如何根据 Adviosr 的相关描述生成代理 Proxy 的?
该部分参考生成 bean 的代理对象 Proxy 并注册执行 DogService 的代理对象;
aop:aspectj-autoproxy element
要让容器能够支持 @AspectJ 注解的方式,<aop:aspectj-autoproxy/> 配置是必须的,那么,本小节将会分析,该配置元素在 Spring 容器的初始化过程是如何被解析的并起到了什么样的作用?
从 Spring Core Container 源码分析七:注册 Bean Definitions 中我们知道,当解析到一些特殊的自定义的元素(比如 <context:component-scan/>等元素)的时候,会进入 parse custom element process 流程;再来看看当时针对 custom element <context:component-scan/> 的有关的详细分析流程,
这里的区别是,custom element 换成了 <aop:aspectj-autoproxy/> 那么对应上述的流程变化有,
- step 1.1.1 element.getNamespaceURI() 返回的是 http://www.springframework.org/schema/aop
- step 1.2 得到的 NamespaceHandler 是 不再是 ContextNamespaceHandler 而是 AopNamespaceHandler
- 所以 step 1.3 handler.parse 部分实际上调用的是 AopNamespaceHandler.parse 相关流程见 parse element by AopNamespaceHandler;
综上所述,配置元素 <aop:aspectj-autoproxy/> 的作用就是去注册AnnotationAwareAspectJAutoProxyCreator
相关联的 post-bean-process definition;
parse element by AopNamespaceHandler
这部分不复杂,主要就是去注册 AnnotationAwareAspectJAutoProxyCreator post-bean-process definition;不画流程图了,直接看源码,
AopNamespaceHandler.parse(element, parserContext) -> AspectJAutoProxyBeanDefinitionParser.parse(element, parserContext)
AspectJAutoProxyBeanDefinitionParser.java
1 |
|
AopNamespaceUtils.java
1 | public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( |
主要逻辑在第一个方法 registerAspectJAnnotationAutoProxyCreatorIfNecessary,内部逻辑就是以名字 org.springframework.aop.config.internalAutoProxyCreator 来注册AnnotationAwareAspectJAutoProxyCreator
所对应的 post-bean-process definition;最后注册到当前的 Spring 容器中;相关核心逻辑如下,
1 | private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { |
解析 @Aspect class 生成 Advisor 对象并注册
该小节描述,如何将 @AspectJ 对象(DogServiceAspect)转换成相应的 Advisor 对象并注册到容器中;
该过程涉及到 AnnotationAwareAspectJAutoProxyCreator 作为 InstantiationAwareBeanPostProcessor 的回调过程;见 Do Get Bean 流程,
解析并注册 @Aspect DogServiceAspect 主要发生在 step 1.3.1.1.1 resolveBeforeInstantiation 过程中,主要是回调了 InstantiationAwareBeanPostProcessor 的 postProcessBeforeInitialization 方法
直接看源码来一步一步的进行分析,
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).java
1 | protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { |
代码第 8 行,applyBeanPostProcessorsBeforeInstantiation 该方法主要是去回调 InstantiationAwareBeanPostProcessor 的 postProcessBeforeInitialization 方法;那么就该方法继续进行深挖,
1 | protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) { |
AnnotationAwareAspectJAutoProxyCreator(AbstractAutoProxyCreator).postProcessBeforeInstantiation
1 |
|
这个回调方法重要了,但是,让人跌眼镜
的是,你万万不会想到最关键且最核心的地方是在代码第 9 行,不去 debug 真不知道,解析并注册 @Aspect DogServiceAspect 到 Spring 容器的逻辑是在 shouldSkip
判断方法中实现的,
1 | if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) |
好吧,我们就进一步看看 shouldSkip 为什么这么妖艳…
AnnotationAwareAspectJAutoProxyCreator(AspectJAwareAdvisorAutoProxyCreator).shouldSkip
1 |
|
好了,最关键的代码出现了,代码第 4 行 findCandidateAdvisors();,为了体现其重要性,单独创建一个小节 find candidate advisors 来进行描述;
find candidate advisors
接上述代码分析的内容;
AnnotationAwareAspectJAutoProxyCreator.java
1 |
|
最关键的代码在第 6 行,this.aspectJAdvisorsBuilder.buildAspectJAdvisors()
BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors
1 | /** |
这段代码是解析 AspectJ-annotated aspect beans,生成相应的 Advisors,并将 Advisors 注册到容器中整个过程的核心所在,下面依次对其进行解析;
首先,通过 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false) 方法的调用,得到了所有的 bean names;既代码第 20 行;
其次,依次遍历 bean names 依次判断是否为 AspectJ-annotated aspect beans,如果发现是 Aspect-annotated bean( 在我们的测试用例中,对应的就是被 @Aspect 所注解的 DogServiceAspect.java ),那么将会根据 per this 或者 singleton 的情况来分别处理,这里我们只来关注 singleton 的方式,从代码第 37 行开始;随后,代码第 41 行,通过方法 this.advisorFactory.getAdvisors(factory) 解析 AspectJ-annotated bean (DogServiceAspect.java) 并生成与之相关的 Advisor 对象,具体参考小节生成 Advisors ;
最后,将上述生成的 advisors 缓存到 this.advisorsCache 和 this.aspectBeanNames 中;
生成 Advisors
下面我们就 this.advisorFactory.getAdvisors(factory) 方法进一步分析,
ReflectiveAspectJAdvisorFactory.getAdvisors
1 |
|
上述代码中,最主要的部分在代码第 12 到 18 行,该部分代码将会根据 @Aspect 类生成相应的 Advisor 对象,
1 | List<Advisor> advisors = new LinkedList<Advisor>(); |
挨个遍历 aspect class 的 methods,针对每个 method 调用 getAdvisor 方法并判断该 method 上是否注释了 pointcut expression,新生成一个 AspectJExpressionPointcut 实例,并将其封装为一个Advisor
对象 InstantiationModelAwarePointcutAdvisorImpl 返回;至此 Advisor 生成
;看下这段 getAdvisor 方法调用的源码
ReflectiveAspectJAdvisorFactory.getAdvisor
1 |
|
总结,
该小节主要是遍历所有的 Class 找到 @Aspect class 并生成对应的 Advisor 对象,最后返回;
生成 bean 的代理对象 Proxy 并注册
本小节考察的是,bean 是如何根据 Advisor 相关描述生成其代理对象 Proxy 并注册的?
该部分工作主要是当AnnotationAwareAspectJAutoProxyCreator
作为 bean-post-processor 的时候,通过其回调方法 postProcessAfterInitialization 完成的;回顾一下,bean-post-processor 的回调过程,它的接口是在 initialize bean 流程中被回调的,详细步骤参考回调 bean-post-processors 接口方法;
我们看看 AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization 方法被回调的内部执行过程;
AnnotationAwareAspectJAutoProxyCreator.java
1 |
|
主要逻辑发生在代码第 6 行,wrapIfNecessary
AnnotationAwareAspectJAutoProxyCreator.java
1 | /** |
核心代码从第 27 行开始,主要做了这么三件事情
找到与该 bean 相关的 Advisors,代码第 27 行,
具体实现细节参考找到与 Bean 相关的 Advisors,这里所需要的 Advisor 就是从解析 @Aspect class 生成 Advisor 对象并注册所生成的;为当前 bean 生成 Proxy 对象,代码第 30 行,
这里要特别注意的是,用 SingletonTargetSource 封装了 bean 并用其作为参数来创建 Proxy,具体实现细节参考生成 bean 的代理对象 Proxy;缓存 Advised bean,代码第 29 行 和 32 行,
返回 proxy 对象,代码第 33 行;
总结,通过AnnotationAwareAspectJAutoProxyCreator
作为 bean-post-processor 回调其接口方法 postProcessAfterInitialization 完成了对相关 bean 的代理实现,生成其所对应的 Proxy 对象;而后,根据创建 bean 的流程,该 Proxy 将会被注入到 BeanFactory 中;也就意味着,当使用ApplicationContext
从容易中根据该 bean name 获取到的 bean 实际上是一个 Proxy 对象;
找到与 Bean 相关的 Advisors
AnnotationAwareAspectJAutoProxyCreator(AbstractAdvisorAutoProxyCreator).java
1 |
|
代码第三行,findEligibleAdvisors
AnnotationAwareAspectJAutoProxyCreator(AbstractAdvisorAutoProxyCreator).java
1 | /** |
找到当前容器中所有的 Advisors 对象,代码第 13 行;
然后依次遍历这些 Advisor,并判断其 pointcut expression 是否可以适用于当前的 bean,如果适用,那么就将 Advisor 添加入 eligibleAdvisors;代码第 15 行
通过 Order 接口对 eligibleAdvisors 进行排序;代码 19 行
可见,当你有多个适配的 Advisor 对象的时候,Order 接口就显得尤为的重要了;
生成 bean 的代理对象 Proxy
1 | /** |
上面的代码就是如何通过 bean 和与其相关的 Advisor 通过接口的方式或者是通过 CGLIB 的方式生成其代理对象 Proxy;这里,我们将“主要关注用接口生成代理对象的过程”;
- 创建 ProxyFactory 实例,代码 L19
解析当前 bean 的相关接口,并设置到当前的 ProxyFactory 实例中;L28
通过如下代码实现1
proxyFactory.addInterface(ifc);
L32,将 Advice 和 MethodInterceptor 统一封装为 Advisors 对象
- L34-36,将 Advisors 注入 proxyFactory 中;
- L38,设置 target source
- L42,设置 Frozen,【12.8】 Manipulating advised objects 有对 frozen 标志的详细描述,frozen 标志设置为 true,那么就不能对 advised 再设置 advisor/advice 了;
- L48,通过 ProxyFactory 实例生成当前 bean 的代理;proxyFactory.getProxy(getProxyClassLoader()); 详情参考通过 ProxyFactory 生成 Proxy 代理对象
通过 ProxyFactory 生成 Proxy 代理对象
先来看看 ProxyFactory 的一张类图,可见其对应三个父类,
整个创建 Proxy 的流程图如下,
首先 ProxyFactory 是一个 prototype 对象;主要步骤如下,
step 1.1.1.1,将 proxyFactory 作为其构造参数创建 JdkDynamicAopProxy 对象,要注意的是,step 1.1.1.1.1 将 proxyFactory 参数赋值给了 this.advised;JdkDynamicProxy 对象非常的关键,里面提供了 InvocationHandler.invoke 回调方法的实现;
step 1.1.2.2,调用 java.lang.reflection.Proxy 的方法 newProxyInstance 创建一个 Proxy 实例,需要注意的有,
参数 proxiedInterfaces:这里不仅仅包含了 DogService 接口,同时包含了 SpringProxy、Advised 以及 DecoratingProxy 接口;这里为什么要提供这些额外的接口,以 Advised 接口为例,为了提供 Proxy 对象相关的 Advice、Interceptors 以及 Advisors 信息等等;至于 java 如何通过 java.lang.reflection.Proxy 创建生成的 Proxy 代理对象,将来打算专门写一个博文来深入分析;
看看 step 1.1.2 getProxy 的源码,
JdkDynamicAopProxy.java
1 |
|
执行 Proxy 方法调用的内部逻辑
该小节主要是从源码的层面来分析在 Spring 容器中是如何通过 DogServiceImpl 的 Proxy 对象来进行调用的,为了清晰的看到整个调用过程,我们将测试用例的相关源码再次粘贴如下,
1 | package org.shangyang.spring.container; |
通过前面的小节生成 bean 的代理对象 Proxy 并注册的分析可知,这里通过 @Autowired 得到的 DogService 是通过生成 bean 的代理对象 Proxy步骤对 DogServiceImpl 生成的代理对象 proxy;所以在 Person.walksDog 方法中所调用的 dogService.walksDog(dog) 是调用的是 DogService 接口的代理实现类 proxy 的 walksDog(dog) 方法;来看有关该 Proxy 的调用流程图,

内部逻辑实在是很复杂,数度想放弃该流程图的制作,但是,最终顽强的毅力还是让我坚持下来了;既然很复杂,又要在这个小节内对其主要功能和流程描述清楚,那么自然就不能过多的涉及相关细节的讲解,将来考虑专门从 Spring AOP 的框架层面,从顶层接口 Advice、Advisor、MethodInterceptor、AopProxy、ReflectiveMethodInvocation 等对象详细深入的剖析 Spring AOP 的内部逻辑;
主要流程已经由红色箭头标注出来了,
从 step 2.1 dogService.walkDog 开始了关于 dogServiceImpl 的 proxy 的调用过程
注意,该步骤中的调用对象 dogService 是注入 Person 实例中的 $Proxy 实例;step 2.1.1 紧接着调用 JdkDynamicAopProxy (Spring AOP 中所封装的 InvocationHandler 对象) 的 invoke 方法,从流程图通过 ProxyFactory 生成 Proxy 代理对象中我们可以看到对象 JdkDynamicAopProxy 是如何被初始化出来的,要特别注意的是,该对象保存了 ProxyFactory 实例;
step 2.1.1.6 初始化 ReflectiveMethodInvocation 实例 invocation,并且将一系列重要的参数 proxy、target、method、args 等保存在该实例中
step 2.1.1.7 调用 ReflectiveMethodInvocation 实例的 proceed 方法,
里面要注意的是, step 2.1.1.7.1 按照 advices 的优先顺序取得了第一个待执行的 advicestep 2.1.1.7.2 开始进行 advice 的相关调用,
step 2.1.1.7.2.3.1 argBinding 该方法是将 args 与 advice 的参数进行匹配并绑定;
step 2.1.1.7.2.3.2.2 adviceMethod.invoke(dogServiceAspect, args:Object[]) 方法开始调用 Aspect 的拦截方法 dogServiceAdvice.aroundWalkService(pjp:ProceedingJoinPoint);该过程要注意的是,args 参数 Object[] 数组中的第一个参数既是 dogServiceAdvice 所需要的 pjp:ProceedingJoinPoint 参数;
step 2.1.1.7.2.3.2.2.1.1 advice before invocation 开始调用 Around Advice 的
before
切入逻辑;step 2.1.1.7.2.3.2.2.1.2.1 this.methodInvocation.proceed 该方法进入对 Target 被代理对象 DogServiceImpl 的调用,step 2.1.1.7.2.3.2.2.1.2.1.1.1.1 walkDog(dog) 通过方法的反射
调用原生
对象 DogServiceImpl 的方法 walkDog(dog);这里要重点关注的是 step 2.1.1.7.2.3.2.2.1.2.1.1.2 重复 2.1.1.7.1 的步骤,继续处理 Advices,正如我在流程图中的备注那样,如果有多个 Advices,在这里就会发生
递归调用
,也就是通过此递归调用
的方式直到将所有的 Advices 处理干净;step 2.1.1.7.2.3.2.2.1.3 advice after invocation 调用 Around Advice 的
after
切入逻辑;
上面调用的大致流程梳理清楚了,总而言之,通过 InvocationHandler JdkDynamicAopProxy 对象开始了对 Advices 方法的调用,处理 Advices 的过程是采用递归调用逐一处理的方式,直到将 before Advices 处理完毕以后,对 Target 进行调用,然后通过递归出栈的过程,逐一调用 Advice after 的逻辑;总体而言,Spring AOP 借助于 Advice 的拦截方法实现用户的织入逻辑;
补充
AnnotationAwareAspectJAutoProxyCreator
从 aop:aspectj-autoproxy element 小节中,我们知道了AnnotationAwareAspectJAutoProxyCreator
对象;下面对该对象进行简要的分析,
可见,其主要实现了 BeanPostProcessor 接口以及两个与其相关的子接口 InstantiationAwareBeanPostProcessor 和 SmartInstantiationAwareBeanPostProcessor;当作为 InstantiationAwareBeanPostProcessor 的时候,其产生的作用参考解析 @Aspect class 生成 Advisor 对象并注册;当作为 BeanPostProcessor 的时候,其产生的作用参考生成 bean 的代理对象 Proxy 并注册
思维导图
<aop:aspectj-autoproxy> -> AnnotationAwareAspectJAutoProxyCreator bean-post-processor
@AspectJ -> Advisor -> Advice
@Proxy ->
TODO, 画一张思维导图来总结各个节点之间的关联关系;