190310-SpringCloud基礎篇AOP之攔截優先順序詳解
前面兩篇分別介紹了AOP的基本使用姿勢和一些高階特性,當時還遺留了一個問題沒有說明,即不同的advice,攔截同一個目標方法時,優先順序是怎樣的,本篇博文將進行詳細分析
- 同一個切面中,不同型別的advice的優先順序
- 同一個切面中,同一種類型的advice優先順序
- 不同切面中,同一型別的advice優先順序
- 不同切面中,不同型別的advice優先順序
I. 統一切面,不同型別ddvice優先順序
在不分析原始碼的前提下,也只能通過實際的case來看優先順序問題了,我們現在設計一下使用例項,通過輸出結果來看對應的優先順序
1. case設計
首先建立被攔截的bean: com.git.hui.boot.aop.order.InnerDemoBean
@Component public class InnerDemoBean { public String print() { try { System.out.println("in innerDemoBean start!"); String rans = System.currentTimeMillis() + "|" + UUID.randomUUID(); System.out.println(rans); return rans; } finally { System.out.println("in innerDemoBean over!"); } } }
接下來寫一個切面,裡面定義我們常見的各種advice
對於aop的使用,有疑問的可以參考: 190301-SpringBoot基礎篇AOP之基本使用姿勢小結
@Component @Aspect public class OrderAspect { @Pointcut("execution(public * com.git.hui.boot.aop.order.*.*())") public void point() { } @Before(value = "point()") public void doBefore(JoinPoint joinPoint) { System.out.println("do before!"); } @After(value = "point()") public void doAfter(JoinPoint joinPoint) { System.out.println("do after!"); } @AfterReturning(value = "point()", returning = "ans") public void doAfterReturning(JoinPoint joinPoint, String ans) { System.out.println("do after return: " + ans); } @Around("point()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { try { System.out.println("do in around before"); return joinPoint.proceed(); } finally { System.out.println("do in around over!"); } } }
2. 測試
使用SpringBoot的專案進行測試aop,使用還是比較簡單的
@SpringBootApplication public class Application { private InnerDemoBean innerDemoBean; public Application(InnerDemoBean innerDemoBean) { this.innerDemoBean = innerDemoBean; this.innerDemoBean(); } private void innerDemoBean() { System.out.println("result: " + innerDemoBean.print()); } public static void main(String[] args) { SpringApplication.run(Application.class); } }
看下上面執行的輸出結果
do in around before do before! in innerDemoBean start! 1552219604035|e9a31f44-6a31-4485-806a-834361842ce1 in innerDemoBean over! do in around over! do after! do after return: 1552219604035|e9a31f44-6a31-4485-806a-834361842ce1 result: 1552219604035|e9a31f44-6a31-4485-806a-834361842ce1
從輸出結果進行反推,我們可以知道統一切面中,advice執行的先後順序如下
II. 同一切面,同一型別切面
正常來講,攔截一個方法時,統一型別的切面邏輯都會寫在一起,那這個case有什麼分析的必要呢?
在我們實際的使用中,同一型別的advice攔截同一個方法的可能性還是很高的,why? 因為多個advice有自己定義的攔截規則,它們之間並不相同,但可能存在交集,比如我們在上面的切面中,再加一個攔截註解的 before advice
1. case設計
依然是上面的 InnerDemoBean
,方法上加一個自定義註解
@AnoDot public String print() { try { System.out.println("in innerDemoBean start!"); String rans = System.currentTimeMillis() + "|" + UUID.randomUUID(); System.out.println(rans); return rans; } finally { System.out.println("in innerDemoBean over!"); } }
然後加一個攔截註解的advice
@Before("@annotation(AnoDot)") public void doAnoBefore(JoinPoint joinPoint) { System.out.println("dp AnoBefore"); }
2. 測試
再次執行前面的case,然後看下輸出結果如下
In NetAspect doAround before! do in around before dp AnoBefore do before! in innerDemoBean start! 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0 in innerDemoBean over! do in around over! do after! do after return: 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0 In NetAspect doAround over! ans: 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0 result: 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0
我們主要看下兩個before,發現 AnoBefore
在前面; 因此這裡的一個猜測,順序就是根據方法命名的順序來的,比如我們再加一個 doXBefore
,然後我們預估輸出結果應該是
do AnoBefore > doBefore > doXBefore
額外新增一個
@Before("@annotation(AnoDot)") public void doXBefore(JoinPoint joinPoint) { System.out.println("dp XBefore"); }
接著就是輸出結果如下,和我們預期一致
3. Order註解嘗試
我們知道有個Order註解可以來定義一些優先順序,那麼把這個註解放在advice方法上,有效麼?實際嘗試一下
@Order(1) @Before(value = "point()") public void doBefore(JoinPoint joinPoint) { System.out.println("do before!"); } @Order(2) @Before("@annotation(AnoDot)") public void doAnoBefore(JoinPoint joinPoint) { System.out.println("dp AnoBefore"); } @Order(3) @Before("@annotation(AnoDot)") public void doXBefore(JoinPoint joinPoint) { System.out.println("dp XBefore"); }
如果註解有效,我們預期輸出結果如下
do Before > do AnoBefore > do XBefore
然後再次執行,看下輸出結果是否和我們預期一樣
4. 小結
同一個切面中,相同的型別的advice,優先順序是根據方法命名來的,加 @Order
註解是沒有什麼鳥用的,目前也沒有搜尋到可以調整優先順序的方式
III. 不同切面,相同型別的advice
如果說上面這種case不太好理解為啥會出現的話,那麼這個可能就容易理解多了;畢竟一個切面完成一件事情,出現相同的advice就比較常見了;
比如spring mvc中,我們通常會實現的幾個切面
- 一個before advice的切面,實現輸出請求日誌
- 一個before advice的切面,實現安全校驗(這種其實更常見的是放在filter/intercept中)
1. case設計
現在就需要再加一個切面,依然以before advice作為case
@Aspect @Component public class AnotherOrderAspect { @Before("@annotation(AnoDot)") public void doBefore() { System.out.println("in AnotherOrderAspect before!"); } }
2. 測試
接下來看測試輸出結果如下圖
發現了一個有意思的事情了, AnotherOrderAspect
切面的輸出,完全在 OrderAspect
切面中所有的advice之前,接著我們再次嘗試使用 @Order
註解來試試,看下會怎樣
@Order(0) @Component @Aspect public class OrderAspect { } @Aspect @Order(10) @Component public class AnotherOrderAspect { }
如果順序有關,我們預期的輸出結果應該是
do AnoBefore > do Before > doXBefore > do AnotherOrderAspect before!
實際測試輸出如下,和我們預期一致
3. 小結
從上面的測試來看,不同的切面,預設順序實際上是根據切面的命令來的;
Order
IV. 不同切面,不同advice順序
其實前面的case已經可以說明這個問題了,現在稍稍豐富一下 AnotherOrderAspect
,看下結果
1. case設計
@Aspect @Order(10) @Component public class AnotherOrderAspect { @Before("@annotation(AnoDot)") public void doBefore() { System.out.println("in AnotherOrderAspect before!"); } @After("@annotation(AnoDot)") public void doAfter(JoinPoint joinPoint) { System.out.println("do AnotherOrderAspect after!"); } @AfterReturning(value = "@annotation(AnoDot)", returning = "ans") public void doAfterReturning(JoinPoint joinPoint, String ans) { System.out.println("do AnotherOrderAspect after return: " + ans); } @Around("@annotation(AnoDot)") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { try { System.out.println("do AnotherOrderAspect in around before"); return joinPoint.proceed(); } finally { System.out.println("do AnotherOrderAspect in around over!"); } } }
2. 測試
看下執行後的輸出結果
假設A切面優先順序高於B切面,那麼我們執行先後順序如下
V. 小結
本篇內容有點多,針對前面的測試以及結果分析,給出一個小結,方便直接獲取最終的答案
1. 不同advice之間的優先順序順序
around 方法執行前程式碼>before > 方法執行 > around方法執行後代碼 > after > afterReturning/@AfterThrowing
2. 統一切面中相同advice
統一切面中,同類型的advice的優先順序根據方法名決定,暫未找到可以控制優先順序的使用方式
3. 不同切面優先順序
不同切面優先順序,推薦使用 @Order
註解來指定,數字越低,優先順序越高
4. 不同切面advice執行順序
優先順序高的切面中的advice執行順序會呈現包圍優先順序低的advice的情況,更直觀的先後順序,推薦看第四節的順序圖,更加清晰明瞭
VI. 其他
0. 專案
- 工程: https://github.com/liuyueyi/spring-boot-demo
- module: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/010-aop
1. 一灰灰Blog
- 一灰灰Blog個人部落格 https://blog.hhui.top
- 一灰灰Blog-Spring專題部落格 http://spring.hhui.top
一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛
2. 宣告
盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
- 微博地址:小灰灰Blog
- QQ: 一灰灰/3302797840
3. 掃描關注
一灰灰blog
知識星球
打賞 如果覺得我的文章對您有幫助,請隨意打賞。