SpringCloud基礎篇AOP之攔截優先級詳解

相關文章能夠查看: http://spring.hhui.topjava

前面兩篇分別介紹了AOP的基本使用姿式和一些高級特性,當時還遺留了一個問題沒有說明,即不一樣的advice,攔截同一個目標方法時,優先級是怎樣的,本篇博文將進行詳細分析git

  • 同一個切面中,不一樣類型的advice的優先級
  • 同一個切面中,同一種類型的advice優先級
  • 不一樣切面中,同一類型的advice優先級
  • 不一樣切面中,不一樣類型的advice優先級

<!-- more -->github

I. 統一切面,不一樣類型ddvice優先級

在不分析源碼的前提下,也只能經過實際的case來看優先級問題了,咱們如今設計一下使用實例,經過輸出結果來看對應的優先級spring

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

接下來寫一個切面,裏面定義咱們常見的各類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!");
        }
    }
}

2. 測試

使用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執行的前後順序以下學習

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

知識星球

goals

相關文章
相關標籤/搜索