問題的提出
按照自己的思路研究Spring AOP原始碼【1】 這篇文章介紹了Spring AOP原始碼的核心流程,根據這篇文章最後提出的問題,我們來探討一下為什麼通知順序是不一樣的。
首先我們看一下新版本(5.3.5-SNAPSHOT)的通知順序與輸出結果,如下圖:
順序:Around Before After AfterReturning
輸出如下:
===== Around before =====
===== Before =====
do service
===== AfterReturning =====
===== After =====
===== Around after =====
我們再來看一下舊版本(5.2.6.RELEASE)的通知順序與輸出結果,如下圖:
順序:AfterReturning After Around Before
===== Around before =====
===== Before =====
do service
===== Around after =====
===== After =====
===== AfterReturning =====
-
我們看到不同的順序是對結果的輸出也是有影響的
-
還有一個就是對After通知是否執行的影響,我們都知道After通知的定義是不管方法有沒有拋異常,它都會執行
但是如果我們在Before通知或者Around通知中拋一個異常,那上面的兩種排序對於After通知是否執行是不一樣的,具體的執行結果我們來看一下:
我們先在Before通知中拋一個異常,程式碼如下:
@Before("pointCut()")
public void methodBefore() {
System.out.println("===== Before =====");
throw new RuntimeException();
}
下面是不同版本的執行結果:
新版本如下:
===== Around before =====
===== Before =====
Exception in thread "main" java.lang.RuntimeException
老版本如下:
===== Around before =====
===== Before =====
===== After =====
Exception in thread "main" java.lang.RuntimeException
從上面的結果可以看出,新版本的After通知是沒有執行的,而老版本的After通知是執行了的,這就是通知順序所導致的後果,所以小夥伴們在開發時碰到此類問題時,可以往這方面想想喔~
哪一步導致了順序的改變
我們以舊版本來debug,原因到後面就知道了,所以以下的debug流程是基於舊版本的,我們來看看這個順序是一直不變的呢,還是在某個方法執行之後發生了變化
我們就從JdkDynamicAopProxy.invoke
開始,一步一步的debug
,一起來看看順序的變化。
在debug
的時候,有一個極其方便的技巧,那就是:我畫出的那個圖示:drop frame
,這個功能就是返回上一個函式,這樣的話就不用重新執行然後重新打斷點,就相當於一個後悔藥,果然程式的世界和現實的世界是不一樣的,你可以為所欲為,好,我們要做好心理準備開始debug
了。
首先,我們看一下這個chain
是怎麼生成的,進到AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice
:
我們可以看到這個鏈通過getInterceptorsAndDynamicInterceptionAdvice
方法獲得,並放到了一個快取裡
進到DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice
:
看到有個advisors
陣列,最終的鏈就是根據這個陣列得來的,發現這個順序就是最終順序,可以看到這個陣列是由config.getAdvisors()
得來的
進到AdvisedSupport.getAdvisors
:
我們可以看到返回的陣列是this.advisorArray
,那我們來猜一下誰會把這個陣列填充好
嘗試在AdvisedSupport
這個類裡搜尋addAdvisor
,發現有11處:
在這些地方都打上斷點
,然後重新執行,看看具體會執行到哪個位置
發現是在AdvisedSupport.addAdvisors(Advisor... advisors)
這個方法:
這一步的結果還是最終順序,我們想知道誰呼叫了這個方法,這時候就可以用drop frame
了
可以看到在AbstractAutoProxyCreator.createProxy
呼叫了此方法:
可以看到資料是由specificInterceptors
得來的,並且依舊是最終順序,繼續drop frame
可以看到在AbstractAutoProxyCreator.wrapIfNecessary
呼叫了此方法:
可以看到呼叫getAdvicesAndAdvisorsForBean
這個方法獲得了陣列
進到AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean
這個方法:
進到AbstractAdvisorAutoProxyCreator.findEligibleAdvisors
這個方法:
在這裡,我們發現順序和最終順序不一樣了,終於找到你
來看一下後面的返回結果:
從上面的結果,我們可以猜測是sortAdvisors
這個方法改變了順序
進到AbstractAdvisorAutoProxyCreator.sortAdvisors
這個方法:
從上圖可以看出,是PartialOrder.sort(partiallyComparableAdvisors)
這個方法改變了順序
為了確認一下,我們看一下新版的順序是怎麼樣的
AbstractAdvisorAutoProxyCreator.findEligibleAdvisors
中sortAdvisors
之前的結果:
AbstractAdvisorAutoProxyCreator.findEligibleAdvisors
中sortAdvisors
之後的結果:
經觀察,新版的順序沒有變化
AbstractAdvisorAutoProxyCreator.sortAdvisors()
方法
現在我們知道是哪個地方把順序改變了,那我們就看一下PartialOrder.sort
這個方法
public class PartialOrder {
public static <T extends PartialComparable> List<T> sort(List<T> objects) {
// lists of size 0 or 1 don't need any sorting
if (objects.size() < 2) {
return objects;
}
// ??? we might want to optimize a few other cases of small size
// ??? I don't like creating this data structure, but it does give good
// ??? separation of concerns.
List<SortObject<T>> sortList = new LinkedList<SortObject<T>>();
for (Iterator<T> i = objects.iterator(); i.hasNext();) {
addNewPartialComparable(sortList, i.next());
}
// System.out.println(sortList);
// now we have built our directed graph
// use a simple sort algorithm from here
// can increase efficiency later
// List ret = new ArrayList(objects.size());
final int N = objects.size();
for (int index = 0; index < N; index++) {
// System.out.println(sortList);
// System.out.println("-->" + ret);
SortObject<T> leastWithNoSmallers = null;
for (SortObject<T> so: sortList) {
if (so.hasNoSmallerObjects()) {
if (leastWithNoSmallers == null || so.object.fallbackCompareTo(leastWithNoSmallers.object) < 0) {
leastWithNoSmallers = so;
}
}
}
if (leastWithNoSmallers == null) {
return null;
}
removeFromGraph(sortList, leastWithNoSmallers);
objects.set(index, leastWithNoSmallers.object);
}
return objects;
}
}
首先看一下下面的程式碼:
List<SortObject<T>> sortList = new LinkedList<SortObject<T>>();
for (Iterator<T> i = objects.iterator(); i.hasNext();) {
addNewPartialComparable(sortList, i.next());
}
我們從上面的程式碼可以看到,它為會每一個advice構造一個SortObject
結構:
private static class SortObject<T extends PartialComparable> {
T object;
List<SortObject<T>> smallerObjects = new LinkedList<SortObject<T>>();
List<SortObject<T>> biggerObjects = new LinkedList<SortObject<T>>();
}
object
是它自己本身,smallerObjects
包含了比它優先順序高的advice,biggerObjects
包含了比它優先順序低的advice
我們看一下構造結果是這樣的:
根據這個結構,它是怎麼排序的呢?看如下程式碼:
final int N = objects.size();
for (int index = 0; index < N; index++) {
// System.out.println(sortList);
// System.out.println("-->" + ret);
SortObject<T> leastWithNoSmallers = null;
for (SortObject<T> so: sortList) {
if (so.hasNoSmallerObjects()) {
if (leastWithNoSmallers == null || so.object.fallbackCompareTo(leastWithNoSmallers.object) < 0) {
leastWithNoSmallers = so;
}
}
}
if (leastWithNoSmallers == null) {
return null;
}
removeFromGraph(sortList, leastWithNoSmallers);
objects.set(index, leastWithNoSmallers.object);
}
boolean hasNoSmallerObjects() {
return smallerObjects.size() == 0;
}
每次找到smallerObjects物件size為0的物件,也就是此時它是優先順序最高的
從上面的結果圖可以看出,首先是ExposeInvocationInterceptor這個類,這個類是個擴充套件的advisor,它優先順序最高,所以它的smallerObjects的大小為0,
所以它的順序就是0,它被敲定之後,就會被移除掉,別人的smallerObjects和biggerObjects會把它移除掉,
然後AspectJAfterReturningAdvice的smallerObjects就會刪除它,它的smallerObjects的size就會變成0,之後它就是順序1
依次類推,AspectJAfterAdvice為順序2 AspectJAroundAdvice為順序3 最後AspectJMethodBeforeAdvice為順序4。
那我們再來看一下SortObject
是怎麼生成的,看下面的程式碼:
public class PartialOrder {
private static <T extends PartialComparable> void addNewPartialComparable(List<SortObject<T>> graph, T o) {
SortObject<T> so = new SortObject<T>(o);
for (Iterator<SortObject<T>> i = graph.iterator(); i.hasNext();) {
SortObject<T> other = i.next();
so.addDirectedLinks(other);
}
graph.add(so);
}
}
private static class SortObject<T extends PartialComparable> {
void addDirectedLinks(SortObject<T> other) {
int cmp = object.compareTo(other.object);
if (cmp == 0) {
return;
}
if (cmp > 0) {
this.smallerObjects.add(other);
other.biggerObjects.add(this);
} else {
this.biggerObjects.add(other);
other.smallerObjects.add(this);
}
}
}
從上面程式碼,我們可以看出,其實就是根據compareTo
方法進行比較,以此來判斷新增到誰的smallerObjects
和biggerObjects
裡面
關於compareTo
的呼叫,最終跟蹤到了如下方法(同一切面下advice優先順序的比較):
class AspectJPrecedenceComparator implements Comparator<Advisor> {
private int comparePrecedenceWithinAspect(Advisor advisor1, Advisor advisor2) {
boolean oneOrOtherIsAfterAdvice =
(AspectJAopUtils.isAfterAdvice(advisor1) || AspectJAopUtils.isAfterAdvice(advisor2));
int adviceDeclarationOrderDelta = getAspectDeclarationOrder(advisor1) - getAspectDeclarationOrder(advisor2);
if (oneOrOtherIsAfterAdvice) {
// the advice declared last has higher precedence
if (adviceDeclarationOrderDelta < 0) {
// advice1 was declared before advice2
// so advice1 has lower precedence
return LOWER_PRECEDENCE;
}
else if (adviceDeclarationOrderDelta == 0) {
return SAME_PRECEDENCE;
}
else {
return HIGHER_PRECEDENCE;
}
}
else {
// the advice declared first has higher precedence
if (adviceDeclarationOrderDelta < 0) {
// advice1 was declared before advice2
// so advice1 has higher precedence
return HIGHER_PRECEDENCE;
}
else if (adviceDeclarationOrderDelta == 0) {
return SAME_PRECEDENCE;
}
else {
return LOWER_PRECEDENCE;
}
}
}
private int getAspectDeclarationOrder(Advisor anAdvisor) {
AspectJPrecedenceInformation precedenceInfo =
AspectJAopUtils.getAspectJPrecedenceInformationFor(anAdvisor);
if (precedenceInfo != null) {
return precedenceInfo.getDeclarationOrder();
}
else {
return 0;
}
}
}
public abstract class InstantiationModelAwarePointcutAdvisorImpl {
@Override
public int getDeclarationOrder() {
return this.declarationOrder;
}
}
可以看到,其實比較的就是declarationOrder
這個欄位
通過檢視呼叫鏈,檢視在哪裡賦值了這個欄位,如下圖:
發現是ReflectiveAspectJAdvisorFactory.getAdvisors
中賦的值,如下圖:
在舊版中,賦值的大小是advisors的size
由於advisor一個一個的被新增進去的,所以它們的值依次是0,1,2,3,驗證結果如下圖:
我們再來看一下新版的賦值:
我們看到新版的賦值都是0,這樣的話,那大家的優先順序都是一樣的,所以就是按照預設順序來進行執行的,
那這個預設的順序又是怎麼來的呢
通知是從切面的advice
方法提取出來的,並做了一下排序,具體如下:
來看一下排序的比較器:
那麼比較器是怎麼比較的呢
public class ConvertingComparator<S, T> implements Comparator<S> {
@Override
public int compare(S o1, S o2) {
T c1 = this.converter.convert(o1);
T c2 = this.converter.convert(o2);
return this.comparator.compare(c1, c2);
}
}
public class InstanceComparator<T> implements Comparator<T> {
@Override
public int compare(T o1, T o2) {
int i1 = getOrder(o1);
int i2 = getOrder(o2);
return (Integer.compare(i1, i2));
}
private int getOrder(@Nullable T object) {
if (object != null) {
for (int i = 0; i < this.instanceOrder.length; i++) {
if (this.instanceOrder[i].isInstance(object)) {
return i;
}
}
}
return this.instanceOrder.length;
}
public InstanceComparator(Class<?>... instanceOrder) {
Assert.notNull(instanceOrder, "'instanceOrder' array must not be null");
this.instanceOrder = instanceOrder;
}
}
從上面可以分析出,就是在instanceOrder這個陣列裡面的位置,而這個又是通過下面的建構函式賦值的
new InstanceComparator<>(Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class)
所以就是按照這個順序來排序的
總結
-
是因為導致順序不一致的呢?是spring的版本導致的,如下圖:
-
低版本賦予了優先順序,而高版本的沒有賦予優先順序,採用的預設順序,那麼預設循序是什麼呢,如下圖: