1. 程式人生 > >SpringCloud基礎篇AOP之攔截優先順序詳解

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執行的先後順序如下

IMAGE

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");
}

接著就是輸出結果如下,和我們預期一致

IMAGE

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

然後再次執行,看下輸出結果是否和我們預期一樣

IMAGE

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. 測試

接下來看測試輸出結果如下圖

IMAGE

發現了一個有意思的事情了,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!

實際測試輸出如下,和我們預期一致

IMAGE

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. 測試

看下執行後的輸出結果

IMAGE

假設A切面優先順序高於B切面,那麼我們執行先後順序如下

IMAGE

V. 小結

本篇內容有點多,針對前面的測試以及結果分析,給出一個小結,方便直接獲取最終的答案

1. 不同advice之間的優先順序順序

around 方法執行前程式碼  >  before > 方法執行 > around方法執行後代碼 > after > afterReturning/@AfterThrowing

2. 統一切面中相同advice

統一切面中,同類型的advice的優先順序根據方法名決定,暫未找到可以控制優先順序的使用方式

3. 不同切面優先順序

不同切面優先順序,推薦使用 @Order註解來指定,數字越低,優先順序越高

4. 不同切面advice執行順序

優先順序高的切面中的advice執行順序會呈現包圍優先順序低的advice的情況,更直觀的先後順序,推薦看第四節的順序圖,更加清晰明瞭

VI. 其他

0. 專案

1. 一灰灰Blog

一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛

2. 宣告

盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

3. 掃描關注

一灰灰blog

QrCode

知識星球

相關推薦

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), 顧名思義,就是很樂觀,每次去拿數據的時候都認