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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...