学习源码更多的是学习源码的设计,并不像学习应用那样快速上手。
应用学习有练习有反馈,学习起来并不会感觉到枯燥感,但是源码学习就枯燥多了。
源码并不能让你写出一段代码来,只能通过阅读调试等方式去理解别人的代码和设计思路,难度可想而知。
Spring AOP 的源码学习是需要我们层层递进,或者是拆解学习,然后才是整体的一个理解。
学习源码不要想着一步到位,也不要想着一遍就会,不要有焦虑的情绪,逐步的学习会让你逐渐理解原先不懂的知识。
Spring AOP 主要包含
Spring AOP 拆解成这几个步骤进行学习,逐步理解,最后整合。这篇文章主要分析后三个步骤:解析、织入、执行
这里分析的主要时注解形式的切面,所以需要学习的类是 Bean 后置处理器:AnnotationAwareAspectJAutoProxyCreator
Spring 切面解析对应的切入点是 Bean 后置处理器的方法:postProcessBeforeInstantiation
,该方法会在 Bean 创建实例化之前。实际上切面的解析操作只会在第一个 Bean 实例化之前执行,后续的 Bean 创建虽然调用该方法,但是都是从缓存中拿去切面,意味着是切面解析只会执行一次。
描述整个过程中的关键点
AnnotationAwareAspectJAutoProxyCreator
完成 AOP 的解析AnnotationAwareAspectJAutoProxyCreator
继承 AbstractAutoProxyCreator
的方法 postProcessBeforeInstantiation
作为 Spring 扩展点入口postProcessBeforeInstantiation
)中的代码:shouldSkip(beanClass, beanName)
时会调用具体实现类AspectJAwareAdvisorAutoProxyCreator
的实现shouldSkip
)中的代码:findCandidateAdvisors()
时会调用具体实现类AnnotationAwareAspectJAutoProxyCreator
的实现findCandidateAdvisors
)中的代码:this.aspectJAdvisorsBuilder.buildAspectJAdvisors()
时执行注解 AOP 的解析工作buildAspectJAdvisors
中会有一个属性 aspectBeanNames
作为缓存标识,只会在第一次执行该方法时解析注解切面@Aspect
的 Bean)执行 this.advisorFactory.getAdvisors(factory)
解析切面内的所有通知并排序大体上的解析流程就是这样,理解整体流程,剩余的细节就需要自己查看源码了。例如具体的通知解析逻辑、是否是切面的判断、切面排序、默认切面(后续执行时都会使用到)等等
AOP 代理的创建涉及的类还是 Bean 后置处理器:AnnotationAwareAspectJAutoProxyCreator
,不同的是 Spring 拓展点,即对应的入口方法不同
AOP 代理的创建不止有一个入口,所以先单独提出来说一下 AOP 创建动态代理的入口
AnnotationAwareAspectJAutoProxyCreator
继承 AbstractAutoProxyCreator
的方法 postProcessAfterInitialization
作为 Spring 扩展点入口。这是普通 Bean 创建 AOP 动态代理的入口AnnotationAwareAspectJAutoProxyCreator
继承 AbstractAutoProxyCreator
的方法 getEarlyBeanReference
作为 Spring 扩展点入口。这是出现循环依赖是 Bean 创建 AOP 动态代理的入口AnnotationAwareAspectJAutoProxyCreator
继承 AbstractAutoProxyCreator
的方法 postProcessBeforeInstantiation
作为 Spring 扩展点入口。这是针对 TargetSource
创建 AOP 动态代理的入口以上是 Spring AOP 创建动态代理的几个拓展点位置。接下来就是具体创建 Bean 关于 AOP 的动态代理的整体逻辑了。
postProcessAfterInitialization
、getEarlyBeanReference
)执行到代码:wrapIfNecessary(bean, beanName, cacheKey)
postProcessBeforeInstantiation
)优点特殊,不是通过执行方法 wrapIfNecessary
的方式,而是绕过了 wrapIfNecessary
方法调用直接执行代码 createProxy(beanClass, beanName, specificInterceptors, targetSource)
去创建动态代理wrapIfNecessary
)执行到代码:getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null)
时会调用具体实现类AbstractAdvisorAutoProxyCreator
的实现来获取需要织入的通知(切面)getAdvicesAndAdvisorsForBean
)执行到代码:findEligibleAdvisors(beanClass, beanName)
findEligibleAdvisors
)执行到代码:findCandidateAdvisors()
时会调用具体实现类AnnotationAwareAspectJAutoProxyCreator
的实现来获取所有有效的通知findEligibleAdvisors
)执行到代码:findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName)
从所有通知中筛选出有效的通知findAdvisorsThatCanApply
)执行到代码:AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass)
交给工具类完成筛选工作AopUtils
)中最后会来到 org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)
由 AspectJ 完成完成初筛(类匹配:pc.getClassFilter().matches(targetClass)
)和精筛(表达式匹配:methodMatcher.matches(method, targetClass)
)findEligibleAdvisors
)执行到代码:extendAdvisors(eligibleAdvisors)
时会调用具体实现类AspectJAwareAdvisorAutoProxyCreator
的实现补充一个通知(ExposeInvocationInterceptor
)用于执行时递归调用各个通知findEligibleAdvisors
)执行到代码:sortAdvisors(eligibleAdvisors)
来对通知集合排序,方便执行时递归调用(责任链模式)wrapIfNecessary
)执行到代码:createProxy(beanClass, beanName, specificInterceptors, targetSource)
会执行具体的动态代理创建(由 CGLIB CglibAopProxy
或者 JDK,具体的可能会受配置影响如:proxyTargetClass
)配合一张图以作理解
因为被织入通知的 Bean 会有动态代理的存在,当 Bean 被调用时将会由动态代理完成相关通知的调用。
这里以 CGLIB 的动态代理举例,所以创建的动态代理是类 CglibAopProxy.DynamicAdvisedInterceptor
。
intercept
方法执行到代码:new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed()
时会调用其父类 ReflectiveMethodInvocation
的实现正式开始调用相关通知ReflectiveMethodInvocation.proceed
)执行到代码:((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this)
调用第一个通知(ExposeInvocationInterceptor
)并将 this
--CglibMethodInvocation
的调用者传入ExposeInvocationInterceptor.proceed
)执行到代码:invocation.set(mi)
向本地线程变量设置 MethodInvocation
(调用者 CglibMethodInvocation
)ExposeInvocationInterceptor.proceed
)执行到代码:mi.proceed()
继续执行调用者 CglibMethodInvocation
的 proceed()
方法取出下一个通知并调用 (((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this)
)CglibMethodInvocation.proceed
)执行到代码:invokeJoinpoint()
调用真正的目标方法所有的通知都是由调用者 CglibMethodInvocation
通过方法 proceed
完成逐个调用,具体的通知会封装成MethodInterceptor
的实现(MethodBeforeAdviceInterceptor
、AroundInterceptor
等等)方便统一执行调用。
整体上 Spring AOP 的流程就是这样,具体的还有很多细节就不再这里展开分析。源码的学习还是需要从浅入深从初略到细节,才不会那么枯燥。经过这一篇文章的讲解,应该说 Spring AOP 的源码学习才正式入门了,后续的细节可以自己以翻阅源码或者断言调试的方式进行学习。