相關文章能夠查看: http://spring.hhui.topjava
前面兩篇分別介紹了AOP的基本使用姿式和一些高級特性,當時還遺留了一個問題沒有說明,即不一樣的advice,攔截同一個目標方法時,優先級是怎樣的,本篇博文將進行詳細分析git
<!-- more -->github
在不分析源碼的前提下,也只能經過實際的case來看優先級問題了,咱們如今設計一下使用實例,經過輸出結果來看對應的優先級spring
首先建立被攔截的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!"); } } }
接下來寫一個切面,裏面定義咱們常見的各類advicemvc
對於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!"); } } }
使用SpringBoot的項目進行測試aop,使用仍是比較簡單的dom
@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); } }
看下上面執行的輸出結果spring-boot
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執行的前後順序以下學習
正常來說,攔截一個方法時,統一類型的切面邏輯都會寫在一塊兒,那這個case有什麼分析的必要呢?測試
在咱們實際的使用中,同一類型的advice攔截同一個方法的可能性仍是很高的,why? 由於多個advice有本身定義的攔截規則,它們之間並不相同,但可能存在交集,好比咱們在上面的切面中,再加一個攔截註解的before advice
依然是上面的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"); }
再次執行前面的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"); }
接着就是輸出結果以下,和咱們預期一致
咱們知道有個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
而後再次執行,看下輸出結果是否和咱們預期同樣
同一個切面中,相同的類型的advice,優先級是根據方法命名來的,加@Order
註解是沒有什麼鳥用的,目前也沒有搜索到能夠調整優先級的方式
若是說上面這種case不太好理解爲啥會出現的話,那麼這個可能就容易理解多了;畢竟一個切面完成一件事情,出現相同的advice就比較常見了;
好比spring mvc中,咱們一般會實現的幾個切面
如今就須要再加一個切面,依然以before advice做爲case
@Aspect @Component public class AnotherOrderAspect { @Before("@annotation(AnoDot)") public void doBefore() { System.out.println("in AnotherOrderAspect before!"); } }
接下來看測試輸出結果以下圖
發現了一個有意思的事情了,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!
實際測試輸出以下,和咱們預期一致
從上面的測試來看,不一樣的切面,默認順序其實是根據切面的命令來的;
Order
註解來解決不一樣切面的優先級問題,依然是值越小,優先級越高其實前面的case已經能夠說明這個問題了,如今稍稍豐富一下AnotherOrderAspect
,看下結果
@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!"); } } }
看下執行後的輸出結果
假設A切面優先級高於B切面,那麼咱們執行前後順序以下
本篇內容有點多,針對前面的測試以及結果分析,給出一個小結,方便直接獲取最終的答案
around 方法執行前代碼 > before > 方法執行 > around方法執行後代碼 > after > afterReturning/@AfterThrowing
統一切面中,同類型的advice的優先級根據方法名決定,暫未找到能夠控制優先級的使用方式
不一樣切面優先級,推薦使用 @Order
註解來指定,數字越低,優先級越高
優先級高的切面中的advice執行順序會呈現包圍優先級低的advice的狀況,更直觀的前後順序,推薦看第四節的順序圖,更加清晰明瞭
一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
一灰灰blog
知識星球