Spring AOP中增強Advice的執行順序

来源:https://www.cnblogs.com/longtianbin/archive/2022/11/15/16892993.html
-Advertisement-
Play Games

Spring AOP中增強Advice的執行順序 Spring AOP中Advice分類 同一Apsect中不同類型Advice執行順序 配置基礎環境 實驗結果 結論 不同Aspect中Advice執行順序 實驗一: Aspect1為高優先順序,Aspect2為低優先順序 實驗結果 實驗二: Aspec ...


Spring AOP中增強Advice的執行順序

本文主要驗證Spring AOP中Advice的執行順序問題。(Spring版本: 5.3.23)

Spring AOP中Advice分類

Spring AOP中Advice可分為如下五類:

  1. @Around
  2. @Before
  3. @AfterReturning
  4. @AfterThrowing
  5. @After

Advice相關概念參考

同一Apsect中不同類型Advice執行順序

配置基礎環境

  1. 依賴版本
  • Spring 版本為: 5.3.23
  • Spring Boot 版本為: 2.6.12
  • aspectjweaver 版本: 1.9.9.1
  1. 定義Spring Boot啟動類
package sakura.springinaction;

@SpringBootApplication
@EnableAspectJAutoProxy
public class MySpringApplication {
	public static void main(String[] args) {
		SpringApplication.run(MySpringApplication.class, args);
	}
}
  1. 定義一個用於測試的Controller類
package sakura.springinaction.controller;

@Controller
@Slf4j
public class IndexController {
	@GetMapping("/time")
	@ResponseBody
	public String time() {
		LocalDateTime now = LocalDateTime.now();
		String nowTime = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
		log.info("Current time: " + nowTime);
		return nowTime;
	}
}
  1. 定義一個聲明式切麵 Apsect1
@Slf4j
@Component
@Aspect
public class Aspect1 {

   // 定義 Point Cut 切麵
   @Pointcut("execution(public * sakura.springinaction.controller.*.*(..))")
   public void controllerLayer() {
   }

   // 定義Advice

   @Before("controllerLayer()")
   private void beforeAdvice2() {
   	log.info("Aspect_1 # @Before");
   }

   @After("controllerLayer() && @annotation(getMapping)")
   private void afterAdvice1(GetMapping getMapping) {
   	log.info("Aspect_1 # @afterAdvice" + " path: " + Arrays.toString(getMapping.value()));
   }

   @AfterReturning(pointcut = "controllerLayer()", returning = "val")
   private void afterReturningAdvice(Object val) {
   	log.info("Aspect_1 # @AfterReturning" + " returnValue: " + val);
   }

   @AfterThrowing(pointcut = "controllerLayer()", throwing = "thrower")
   private void afterThrowingAdvice(Throwable thrower) {
   	log.info("Aspect_1 # @AfterThrowing" + " thrower: " + thrower.getClass().getName());
   }

   @Around("controllerLayer() && @annotation(getMapping)")
   private Object aroundAdvice(ProceedingJoinPoint pjp, GetMapping getMapping) throws Throwable {
   	// Around 前置處理
   	Stopwatch stopwatch = Stopwatch.createStarted();
   	log.info("Aspect_1 # @Around-Before" + " methodName: " + pjp.getSignature().getName() + ", path: " + Arrays.toString(getMapping.value()));
   	Object result = pjp.proceed();
   	// Around 後置處理
   	log.info("Aspect_1 # @Around-After" + " methodName: " + pjp.getSignature().getName() + ", runTime: " + stopwatch.elapsed(TimeUnit.NANOSECONDS));
   	return result;
   }

}

實驗結果

在 發起請求(http://localhost:8080/time) 後,日誌輸出如圖:
日誌記錄

結論

在同一個切麵(Apsect)定義中對於同一個Join Point而言,不同類型的Advice執行先後順序依次是:

  1. @Around 前置處理
  2. @Before
  3. @AfterReturning/@AfterThrowing
  4. @After
  5. @Around 後置置處理

優先順序說明:

  • 對於進入Join PointAdvice而言(比如: @Around 前置處理,@Before),優先順序越高,越先執行;
  • 對於從Join Point出來的Advice而言(比如: @Around 後置處理,@After),優先順序越高,越後執行;
  • 優先順序從高到低依次為: @Around, @Before,@After,@AfterReturning,@AfterThrowing

PS:

如果在同一個切麵(Apsect)中定義了兩個同類型的Advice(比如定義兩個@Before), 對於某個Join Point而言這兩個Advice都匹配,那麼這兩個Advice執行的先後順序是無法確定的。

不同AspectAdvice執行順序

問: 當不同的Aspect中的Advice 都匹配到了同一個Join Point,那麼那個Aspect中的Advice 先執行,那個後執行呢?

答: 不確定 ,但是可以通過在class上添加註解@Order指定優先順序確定執行順序(參考文檔)

實驗一: Aspect1為高優先順序,Aspect2為低優先順序

  1. Aspect1 類似,再定義一個切麵類Aspect2,如下
package sakura.springinaction.advice;
import org.springframework.core.annotation.Order;

@Slf4j
@Component
@Aspect
@Order(2)
public class Aspect2 {
  // 定義Advice
  @Before("sakura.springinaction.advice.Aspect1.controllerLayer()")
  private void beforeAdvice2() {
    log.info("Aspect_2 # @Before");
  }

  @After("sakura.springinaction.advice.Aspect1.controllerLayer() && @annotation(getMapping)")
  private void afterAdvice1(GetMapping getMapping) {
    log.info("Aspect_2 # @afterAdvice" + " path: " + Arrays.toString(getMapping.value()));
  }

  @AfterReturning(pointcut = "sakura.springinaction.advice.Aspect1.controllerLayer()", returning = "val")
  private void afterReturningAdvice(Object val) {
    log.info("Aspect_2 # @AfterReturning" + " returnValue: " + val);
  }

  @AfterThrowing(pointcut = "sakura.springinaction.advice.Aspect1.controllerLayer()", throwing = "thrower")
  private void afterThrowingAdvice(Throwable thrower) {
    log.info("Aspect_2 # @AfterThrowing" + " thrower: " + thrower.getClass().getName());
  }

  @Around("sakura.springinaction.advice.Aspect1.controllerLayer() && @annotation(getMapping)")
  private Object aroundAdvice(ProceedingJoinPoint pjp, GetMapping getMapping) throws Throwable {
    Stopwatch stopwatch = Stopwatch.createStarted();
    log.info("Aspect_2 # @Around-Before" + " methodName: " + pjp.getSignature().getName() + ", path: " + Arrays.toString(getMapping.value()));
    Object result = pjp.proceed();
    log.info("Aspect_2 # @Around-After" + " methodName: " + pjp.getSignature().getName() + ", runTime: " + stopwatch.elapsed(TimeUnit.NANOSECONDS));
    return result;
  }

}
  1. Aspect1 添加@Order註解指定優先順序,如下
@Slf4j
@Component
@Aspect
@Order(1)
public class Aspect1 {
  //...
}

此時,Aspect1的優先順序比Aspect2的優先順序高。

實驗結果

實驗結果如下:
日誌記錄

說明:

高優先順序的Aspect1中的@Around前置處理和@Before先於低優先順序的Aspect2執行,而@AfterReturning,@After@Around後置處理,則低優先順序的Aspect2先執行。

實驗二: Aspect1為低優先順序,Aspect2為高優先順序

  1. 更改兩個Aspect@Order註解優先順序,如下:
@Slf4j
@Component
@Aspect
@Order(2)
public class Aspect1 {
  //...
}
@Slf4j
@Component
@Aspect
@Order(1)
public class Aspect2 {
	//...
}

實驗結果

實驗結果如下:
日誌記錄

結論

  1. 當不同的Aspect中的Advice 都匹配到了同一個Join Point,不同Aspect中的Advice 執行順序不確定。
  2. 通過在Aspect類上添加註解@Order指定優先順序,確定執行順序,執行順序滿足如下規律
    1. 對於@Around前置處理 和@Before兩種Advice而言,所在的Aspect優先順序越高,越先執行
    2. 對於@AfterReturning@AfterThrowing,@After@Around後置處理 類型的Advice而言,所在的Aspect優先順序越高,越後執行

參考資料:

  1. Aspect Oriented Programming with Spring
  2. AspectJ Programming Guide

本文主要目的是記錄學習過程,加深對知識點理解; 如有行文有誤,望指正。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 小伙伴們曾經可能都經歷過整天寫著CURD的業務,都沒寫過一些組件相關的東西,這篇文章記錄一下SpringBoot如何自定義一個Starter。 原理和理論就不用多說了,可以在網上找到很多關於該方面的資料,這裡主要分享如何自定義。 原文鏈接:SpringBoot怎麼自定義一個Starter ?一隻小C ...
  • 使用 .editorconfig 統一規範 Visual Studio 編碼格式,使用 /utf-8 編譯選項指定源碼文件解碼格式,使得整個團隊文件編碼、代碼格式保持一致。 ...
  • 類的生命周期 首先我們先看類的生命周期 類的載入過程包含了載入、驗證、準備、解析、初始這五個階段,其中除瞭解析階段其他四個階段的發生順序都是確定的,因為解析階段在某些情況下會在初始階段之後開始,同時這些階段都是按順序開始的不是按順序進行或結束,因為這些階段通常都是互相交叉的混合進行。以下為類的生命周 ...
  • 引入課程和Maven 1.Maven maven中央倉庫:Maven Repository: Search/Browse/Explore (mvnrepository.com) maven倉庫是國外的一個網站,由於網路問題,我們也常使用maven倉庫的鏡像 maven的原理和java程式操作資料庫, ...
  • hello,大家好呀,我是既寫 Java 又寫 Go 的小樓,在寫 Go 的過程中經常對比這兩種語言的特性,踩了不少坑,也發現了不少有意思的地方,今天就來聊聊 Go 自帶的 HttpClient 的超時機制。 Java HttpClient 超時底層原理 在介紹 Go 的 HttpClient 超時 ...
  • 這篇文章主要介紹列表的一些知識。 函數list 首先需要說明的是,列表與元組、字元串一樣都是一種序列,但不同的是列表是可變的,即可修改其內容。 因為不能像修改列表那樣修改字元串,所以有些情況下使用字元串來創建列表很有幫助,函數list可以用來創建列表。 >>> list('hello') ['h', ...
  • 一、前言 是這樣的,之前手機備份圖片到電腦,由於蘋果拍照開了Live模式,所以它導出的圖片有一個2秒的視頻(.mov) 跟一張靜態圖(.jpg / .heic),靜態圖輸出取決當時導出的選項。 現在想恢復到手機,導入發現Live圖不能動了。 欸 無非就是找到兩個同名的,然後移到另一個文件夾嘛,一開始 ...
  • Python基礎之網路編程 一、網路編程前戲 1.什麼是網路編程: ​ 網路編程是指基於網路編寫代碼,能夠實現數據的遠程交互 2.學習網路編程的目的: ​ 能夠開發基於網路,實現與多用戶交互的C/S架構的軟體 3.網路編程的起源: ​ 最早起源於美國軍事領域,早期人們想要實現不同電腦內的數據交互只 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...