SpringCloud基礎篇AOP之攔截優先順序詳解
相關文章可以檢視: http://spring.hhui.top
前面兩篇分別介紹了AOP的基本使用姿勢和一些高階特性,當時還遺留了一個問題沒有說明,即不同的advice,攔截同一個目標方法時,優先順序是怎樣的,本篇博文將進行詳細分析
- 同一個切面中,不同型別的advice的優先順序
- 同一個切面中,同一種類型的advice優先順序
- 不同切面中,同一型別的advice優先順序
- 不同切面中,不同型別的advice優先順序
<!-- more -->
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. 小結
從上面的測試來看,不同的切面,預設順序實際上是根據切面的命令來的;
- A切面中的advice會優先B切面中同類型的advice
- 我們可以通過
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
知識星球
相關推薦
SpringCloud基礎篇AOP之攔截優先順序詳解
開發十年,就只剩下這套架構體系了! >>>
SpringBoot基礎篇AOP之基本使用姿勢小結
浪費了“黃金五年”的Java程式設計師,還有救嗎? >>>
SpringBoot基礎篇AOP之高階使用技能
開發十年,就只剩下這套架構體系了! >>>
Java基礎篇:回撥機制詳解
一、什麼是回撥: 回撥是一種雙向的呼叫模式,程式模組之間通過這樣的介面呼叫完成通訊聯絡,回撥的核心就是回撥方將本身即this傳遞給呼叫方,這樣呼叫方就可以在呼叫完畢之後再告訴回撥方它想要知道的資訊。 回撥函式用於層間協作,上層將本層函式安裝在下層,這個函式就是回撥,而下層
JAVA常用集合框架用法詳解基礎篇一之Colletion介面
首先,在學習集合之前我們能夠使用的可以儲存多個元素的容器就是陣列。 下面舉幾個例子主要是引出集合類的: 1、8,4,5,6,7,55,7,8 像這樣的型別相同的可以使用陣列來儲存,本例可以用int[] arr來儲存。 2、”zhnagsan”,true,68 像這樣的可以使
JAVA常用集合框架用法詳解基礎篇三之Colletion子介面Set
這一篇我們來介紹Collection介面的另一個子介面,Set介面。Set是個介面,元素不可以重複,是無序的。Set介面中的方法和Collection的一致。 A、Set的子類: 1、HashSet:此類實現的Set介面,由雜湊表(實際上是一個HashMap)例項支援,它不保證Set的迭代順
JAVA常用集合框架用法詳解基礎篇二之Colletion子介面List
接著上一篇,接著講講集合的知識。上一篇講了Collection介面。它可以說是集合的祖先了,我們這一篇就說說它的子孫們。 一、Collection的子介面 List:有序(存入和取出的順序一致),元素都有索引(即角標),元素可以重複。 Set:元素不能重複,無序的。 首先講講L
史上最簡單MySQL教程詳解(基礎篇)之多表聯合查詢
常用術語 內連線 外連線 左外連線 右外連線 注意事項: 自連線 子查詢 在上篇文章史上最簡單MySQL教程詳解(基礎篇)之資料庫設計正規化及應用舉例我們介紹過,在關係型資料庫中,我們通常為了減少資料的冗餘量將對資料表進行規範,將
《Python基礎篇》之初識Python一
file dff lam lag port nag elong dir car %E4%BD%BF%E7%94%A8CHttpFile%E4%BB%8E%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E6%AD%A3%E7%A1%AE%E7%9A%
spring AOP解析之註解方式詳解
parser 分享 pro asp mes aop log space spec 命名空間處理器是AopNamespaceHandler,我們可以看到這裏註冊了幾個解析器,第一個我們知道是xml形式的解析,接下來我們看AspectJAutoProxyBeanDefiniti
[ 轉載 ] Java多線程系列--“基礎篇”04之 synchronized關鍵字
span 多線程 sync body 關鍵字 .com style 基礎 pos http://www.cnblogs.com/skywang12345/p/3479202.html[ 轉載 ] Java多線程系列--“基礎篇”04之 synchronized關鍵字
申論(基礎篇)之基礎常識復習建議
地形 pos 基礎 ima clas 技術 相關 歷史人物 復習 一、歷史常識復習建議(1~3題) 以歷史人物為中心學習相關的歷史事實、常識。 二、地理知識復習建議(1~3題) 1、自轉和公轉 2、中國三級階梯 3、
Docker 基礎技術之 Linux namespace 詳解
基本 mar $$ 裏的 sta 進程資源 進程間通信 開始 消息隊列 Docker 是“新瓶裝舊酒”的產物,依賴於 Linux 內核技術 chroot 、namespace 和 cgroup。本篇先來看 namespace 技術。 Docker 和虛擬機技術一樣,從操作系
IP地址和子網劃分學習筆記之《知識學習篇:子網劃分詳解》
子網掩碼 IP地址 子網劃分 在學習掌握了前面的IP地址和子網劃分之《進制計數》和IP地址和子網劃分之《IP地址詳解》這兩部分知識後,接下來將學習子網劃分。 一、子網掩碼 要學習子網劃分,首先就要必須知道子網掩碼,只有掌握了子網掩碼這部分內容,才能很好的理解和劃分子網。 1、子網掩碼介紹 子網掩碼
Java 基礎之--註解Annotation詳解
註解 java 基礎 time span face 自定義註解 div ace rtu 自定義註解入門: public @interface Annotation01 { //set default value ""; String value() de
python之路 第二篇 數據類型詳解及其方法
字符 引號 print 成員 移除 join att pri str 字符串 #作用:描述名字,性別,國籍,地址等信息#定義:在單引號\雙引號\三引號內,由一串字符組成 name=‘Matthew‘ #優先掌握的操作: #1、按索引取值(正向取+反向取) :只能取 #2
Java多線程系列---“基礎篇”04之 synchronized關鍵字
www. 時間 pub syn pre 現在 執行流程 什麽 你是 轉自:https://www.cnblogs.com/skywang12345/p/3479202.html (含部分修改) 概要 本章,會對synchronized關鍵字進行介紹。涉及到的內容包括:
Java多線程系列---“基礎篇”07之 線程休眠
技術分享 ring 說明 href super 等於 在線 for logs 轉自:http://www.cnblogs.com/skywang12345/p/3479256.html (含部分修改) 概要 本章,會對Thread中sleep()方法進行介紹。涉及到的內
Java多線程系列---“基礎篇”08之 join()
spa www gif 怎麽 ... try run stack 通過 轉自:http://www.cnblogs.com/skywang12345/p/3479275.html (含部分修改) 概要 本章,會對Thread中join()方法進行介紹。涉及到的內容包括:
Java多線程系列---“基礎篇”13之 樂觀鎖與悲觀鎖
而是 關系型 lock color 情況 發現 mis 再次 中一 轉自:http://www.cnblogs.com/zhengbin/p/5657435.html 樂觀鎖 樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認