相關文章可以檢視: spring.hhui.top
前面兩篇分別介紹了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. 小結
從上面的測試來看,不同的切面,預設順序實際上是根據切面的命令來的;
- 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. 專案
- 工程:github.com/liuyueyi/sp…
- module: github.com/liuyueyi/sp…
1. 一灰灰Blog
- 一灰灰Blog個人部落格 blog.hhui.top
- 一灰灰Blog-Spring專題部落格 spring.hhui.top
一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛
2. 宣告
盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
- 微博地址: 小灰灰Blog
- QQ: 一灰灰/3302797840
3. 掃描關注
一灰灰blog
知識星球