1、通知的执行顺序
网上大量Spring AOP通知的执行顺序的文章,但是可以发现有两种完全不同的说法,第一种说@After在@AfterRetuning之前执行,另外则是截然相反。这个时候可能会疑惑哪一种才是正确的,因为每一种都说的铮铮有词,每一种的都在理。如果研究一下,会发现他们的说法都是正确的,只不过他们讲的版本是不同的。可查看Spring5.2.7的commit的代码(github.com/spring-proj…),这个commit改变了执行顺序。
所以最终结论:
Spring 5.2.7.RELEASE之前的版本正确执行:
graph LR
A[Around]-->B[Before]
B-->C[Around]
C-->D[After]
D-->E[AfterReturning]
Spring 5.2.7.RELEASE之前的版本错误执行:
graph LR
A[Around]-->B[Before]
B-->C[After]
C-->D[AfterThrowing]
Spring 5.2.7.RELEASE之后的版本正确执行:
graph LR
A[Around]-->B[Before]
B-->C[AfterReturning]
C-->D[After]
D-->E[Around]
Spring 5.2.7.RELEASE之后的版本错误执行:
graph LR
A[Around]-->B[Before]
B-->C[AfterThrowing]
C-->D[After]
2、造成5.2.7.RELEASE版本前后执行顺序主要因素
2.1 5.2.6.RELEASE中的ReflectiveAspectJAdvisorFactory
启动加载advisors列表的值:
2.2 5.2.7.RELEASE中的ReflectiveAspectJAdvisorFactory
启动加载advisors列表的值:
2.3 版本的declarationOrder值的对比
各通知的初始化:
| 通知 | 5.2.6.RELEASE中declarationOrder值 | 5.2.7.RELEASE中declarationOrder值 |
|---|---|---|
| Around | 0 | 0 |
| Before | 1 | 0 |
| After | 2 | 0 |
| AfterReturning | 3 | 0 |
| AfterThrowing | 4 | 0 |
3、Advice的获取
想知道5.2.7RELEASE通知的执行顺序变化,先了解一下advice的使用,数据是怎么关联起来。
3.1 调用阶段chain列表的获取
当一个方法被Aspect拦截时,会调用CglibAopProxy.DynamicAdvisedInterceptor#intercept(),方法内会获取所有的advice。
接下来,来看一下getInterceptorsAndDynamicInterceptionAdvice()是如何获取所有的advice。
由方法可知,用目标的方法名作为key去map获取,当获取到为空,则根据AdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice()去生成advice,并设置到map缓存起来。
此方法会遍历所有的增强器,将其转为Interceptor。我们也可得知advisors原始数据来源是Advised。
调用阶段chain的生成到达这步都相对清晰了,接下来我们只要搞懂AdvisedSupport的advisorArray是如何被赋值。
3.2 AdvisedSupport的advisorArray的赋值
说到advisorArray的赋值,那就是需要来到启动时调用AbstractAutoProxyCreator#postProcessAfterInitialization()的位置开始。
再调用内部方法wrapIfNecessary()。
当advice不为空时,去创建代理。创建代理是advisorArray的赋值关键所在。
将specificInterceptors构建成advisor,并赋值给代理方法,下面看代理方法addAdviors做了什么操作。
最关键的是updateAdvisorArray(),就是它赋值给advisorArray。这就跟之前的getAdvisors()联系上了。
4、造成差异主要原因源码解析
首先来看一下5.2.7.RELEASE前后版本调用AspectJAwareAdvisorAutoProxyCreator.sortAdvisors()之后,advisor的变化。
有图可知,5.2.7.RELEASE版本排序之后并没有变化,5.2.6.RELEASE排序之后发生了巨大的变化。
其实sortAdvisors()并没有做代码修改,关键在于前面提到的declarationOrder的值。由于5.2.6.RELEASE排序后有变化,我们使用5.2.6.RELEASE版本能更明显的查看sortAdvisors()的操作。
sortAdvisors()把普通的advisor封装成PartiallyComparableAdvisorHolder用来做排序。下面重点看一下PartialOrder.sort()的代码。
排序之前,先把对象封装成SortObject对象,再排序。来看看addNewPartialComparable()的代码。
关键在于so.addDirectedLinks(other)这句上,addDirectedLinks()的代码。
可以看出这个方法用advisor去比较,两个advisor相等,则不做任何操作;否则,大的advisor把小的advisor添加到它的smallerObjects列表,小的advisor把大的advisor添加到它的biggerObjects列表。现在来看看advice具体怎么比较的。
compare()先去比较advisor的order值,order值相等并且两个advisor在同一方面声明,才使用comparePrecedenceWithinAspect()做比较。
这方法可以看出,两个advisor的declarationOrder不相等的时候,有没有带属于after的切面会产生截然相反的结果。因为5.2.7.RELEASE的declararionOrder全都是0,所以oneOrOtherIsAfterAdvice的值并不重要,都只是返回0,这也可以解释前文提到的调用其实sortAdvisors()排序没有变化。
上面SortObject的生成应该清楚了,又要回到PartialOrder#sort(),查看怎么使用SortObject。
循环去查找没有smallerObjects的SortObject,查询后把该SortObject在sortList移除,并设值到advisor列表。