按照自己的思路研究Spring AOP原始碼【2】

eaglelihh發表於2021-05-19

問題的提出

按照自己的思路研究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.findEligibleAdvisorssortAdvisors之前的結果:

AbstractAdvisorAutoProxyCreator.findEligibleAdvisorssortAdvisors之後的結果:

經觀察,新版的順序沒有變化

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方法進行比較,以此來判斷新增到誰的smallerObjectsbiggerObjects裡面

關於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)
所以就是按照這個順序來排序的

總結

  1. 是因為導致順序不一致的呢?是spring的版本導致的,如下圖:

  2. 低版本賦予了優先順序,而高版本的沒有賦予優先順序,採用的預設順序,那麼預設循序是什麼呢,如下圖:

相關文章