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
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...