Spring入門之AOP實踐:@Aspect + @Pointcut + @Before / @Around / @After

来源:https://www.cnblogs.com/desertfish/archive/2019/08/27/11421260.html
-Advertisement-
Play Games

AOP (Aspect Oriented Programming)是Spring中一種重要的編程思想。AOP涉及Aspect、Advice、Join point、Pointcut、Weaving、Target等多個概念,實現依賴於Spring中的@Aspect、@Pointcut、@Before、@... ...


零、準備知識

1)AOP相關概念:Aspect、Advice、Join point、Pointcut、Weaving、Target等。   ref: https://www.cnblogs.com/zhangzongle/p/5944906.html  有代碼示例 2)相關註解:@Aspect、@Pointcut、@Before、@Around、@After、@AfterReturning、@AfterThrowing  

一、實踐目標

1)@Aspect的功能 2)@Pointcut的切麵表達式 3)@Before、@Around、@After、@AfterReturning / @AfterThrowing的時序關係 4)AOP概念的重新梳理和使用  

二、核心代碼

MainController.java包含兩個測試函數,分別是doSomething() 和 test()。
 1 // MainController.java
 2 @RestController
 3 public class MainController {
 4     RequestMapping(value="/doSomething", method = RequestMethod.POST)
 5     @CrossOrigin("*")
 6     public void doSomething() { 
 7         System.out.println("This is doSomething"); 
 8         test();
 9     }
10 
11     @RequestMapping(value="/justTest", method = RequestMethod.POST)
12     @CrossOrigin("*")
13     public void test() { System.out.println("This is test");}
14 }

 

ExampleAop.java為AOP相關代碼,定義了pointcut和多個Advice函數。

 1 // ExampleAop.java
 2 @Component
 3 @Aspect
 4 @Order(1)
 5 public class ExampleAop {
 6 
 7     private static final Logger LOGGER = LoggerFactory.getLogger(ExampleAop.class);
 8 
 9     // 匹配com.example.demo包及其子包下的所有類的所有方法
10     @Pointcut("execution(* com.example.demo..*.*(..))")
11     public void executeService() {
12     }
13 
14     @Before("executeService()")
15     public void doBeforeAdvice(JoinPoint joinPoint) throws Exception {
16         LOGGER.info("Before [{}]", joinPoint.getSignature().getName());
17     }
18 
19     @Around("executeService()")
20     public void doAroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
21         LOGGER.info("Around1 [{}]", joinPoint.getSignature().getName());
22         joinPoint.proceed();
23         LOGGER.info("Around2 [{}]", joinPoint.getSignature().getName());
24     }
25 
26     @After("executeService()")
27     public void doAfterAdvice(JoinPoint joinPoint) throws Exception {
28         LOGGER.info("After [{}]", joinPoint.getSignature().getName());
29     }
30 
31     @AfterReturning("executeService()")
32     public void doAfterReturningAdvice(JoinPoint joinPoint) throws Exception {
33         LOGGER.info("AfterReturning [{}]", joinPoint.getSignature().getName());
34     }
35 }

 

編譯並運行jar包,調用介面/doSomething。

1 # 調用介面
2 curl localhost:8080/doSomething

 

到伺服器觀察日誌。

1 <!-- 後臺日誌 -->
2 com.example.demo.aop.ExampleAop          : Around1 [doSomething]
3 com.example.demo.aop.ExampleAop          : Before [doSomething]
4 This is doSomething
5 This is test
6 com.example.demo.aop.ExampleAop          : Around2 [doSomething]
7 com.example.demo.aop.ExampleAop          : After [doSomething]
8 com.example.demo.aop.ExampleAop          : AfterReturning [doSomething]

 

三、分析與結論

1)@Aspect的功能   在連接點的前後添加處理。在本例中,doSomething() 是連接點,而test() 不是。   是否只有最外層的joinpoint才會被Advice插入?在後面進行簡單的探討和猜測。     2)@Pointcut的切麵表達式   ref: https://www.cnblogs.com/liaojie970/p/7883687.html  常用表達式   ref: https://www.jianshu.com/p/fbbdebf200c9  完整表達式   @Pointcut("execution(...)") 是Pointcut表達式,executeService() 是point簽名。表達式中可以包含簽名的邏輯運算。     常用表達式:
1 execution(public * com.example.demo.ExampleClass.*(..))  // ExampleClass的所有公有方法
2 execution(* com.example.demo..*.*(..)) // com.example.demo包及其子包下的所有方法
3 logSender() || logMessage() // 兩個簽名的表達式的並集

 

 

3)@Before、@Around、@After、@AfterReturning / @AfterThrowing的時序關係   @Around1 -> @Before -> 方法 -> @Around2 -> @After -> @AfterReturning / @AfterThrowing(時序問題後面有額外討論。)   另外可以發現,@Around是可以影響程式本身執行的,如果不調用 joinPoint.proceed(); 就不會執行方法。其他幾個都無法影響程式執行。     4)AOP概念的重新梳理和使用   Aspect(切麵):使用了@Aspect註解,如ExampleAop類。   Advice(增強):在指定位置進行的增強操作,如方法運行時間統計、用戶登錄、日誌記錄等。由@Before、@After等註解標註,如doBeforeAdvice() 方法。   Weaving(織入):AOP就是一種把Advice織入(即嵌入、插入)到Aspect中指定位置執行的機制。   Join point(連接點):Advice執行的位置,也是Advice的參數,是一個具體的方法。如日誌中看到的doSomething() 函數。   Pointcut(切點):以表達式的形式表示一組join point,用於由@Pointcut註解定義Advice的作用位置。如@Pointcut("execution(* com.example.demo..*.*(..))") 代表com.example.demo包及其子包下的所有類的所有方法。   Target(對象):被增強的對象,即包含主業務邏輯的類的對象,如ExampleAop類的實例。

 

四、疑問與討論

1. 本文說執行順序為@Around1 -> @Before -> 方法 -> @Around2 -> @After,但有的文章中說是@Before -> @Around1 -> 方法 -> @Around2 -> @After,也有說@Around1 -> @Before -> 方法 -> @After -> @Around2,哪個對?     反正代碼跑起來是這個順序,那就是這個順序嘍。每個Advice都加上sleep拉開時間也沒有變化。不知道是否受版本或代碼自身影響。   總之可以得到一個結論:@Before / @After 最好不要和@Around混用,執行順序不好確定。   時序至少總是滿足:@Around1 / @Before -> 方法 -> @Around2 / @After -> @AfterReturning / @AfterThrowing   另外找到一篇支持本文執行順序的文章:https://blog.csdn.net/qq_32331073/article/details/80596084     2. 為何doSomething() 和 test() 都是@Pointcut中選中的作用節點,但只有doSomething() 插入了Advice,而test() 沒有呢?           一個猜測:從字面意思理解,@Pointcut對所有代碼以表達式為規則剪一刀,一側是所有的joinpoint,另一側是普通代碼。在joinpoint與另一側代碼間插入一層Advice的代理,另一側的代碼如果要調用joinpoint,則必須經Advice進行增強操作。而不同的joinpoint在同一側,因此未插入Advice。         有時間再讀源碼瞭解其中的機制。

(一個猜測)

 

五、Future Work

1. AOP中還有Advisor等概念,待學習。 2. 切麵表達式(@Pointcut里的表達式)規則豐富,待學習。 3. Advice的插入時機,待讀源碼學習。

 


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

-Advertisement-
Play Games
更多相關文章
  • 瞭解完網路協議,我們會發現,網路通信的五層模型里,有兩個很重要的概念:IP 地址和 MAC 地址。 那麼 IP 地址是怎麼來的,又是怎麼沒的?MAC 地址與 IP 地址又有什麼區別? 這回答上面問題前,先熱下身,大家知道如何查看本機的 IP 嗎?這個問題,即便是沒有專業學過電腦的人,只要折騰過電腦 ...
  • 今天開始學習設計模式,藉此機會學習並整理學習筆記。 設計模式是一門不區分語言的課程,什麼樣的編程語言都可以用到設計模式。如果說java語法規則比作武功招式的話,那麼設計模式就是心法。 設計模式共有23種,常見的19種,最常用的9-10種。 設計模式分三種類型:創建型、結構型、行為型; 其中創建型包含 ...
  • 在內置函數(dict、list、set、tuple)的基礎上,collections模塊還提供了幾個其他的數據類型:Counter、deque、defaultdict、namedtuple和OrdereDict等 1.namedtuple 作用:用於生成一個可以使用名字訪問元素內容的tuple 如果 ...
  • 思前想後一個月,我終於敲下了我的第一篇開山之作。 博客千千萬,我的博客首先記錄的是學習時候的理解,用於給自己翻閱查找,現在主要研究的是C語言和STM32。如果能幫到你,那是最好的,假如我寫的東西有錯誤歡迎指正,所以請反覆對比,僅作參考。其次,這裡不僅記錄學習,也會寫下我在朋友圈不能公開的想法和秘密! ...
  • 一、共用變數 共用變數:當多個線程訪問同一個變數的時候。會產生共用變數的問題。 例子: 正如上面的結果可以看出:並不是我們期望的0,而是-286705,這就是因為我們共用變數了,同時對變數進行了操作,程式並不是原子的。 2.解決方案:使用“鎖”,“信號燈” (1)鎖lock:是一個標誌,表示一個線程 ...
  • 文件上傳 表單 指定<form>的method="post", enctype="multipart/form-data"。 accept指定文件類型,有多種類型時逗號分隔,multiple指定可以選擇多個文件。 傳統處理方式 選擇的文件是放在請求消息體中的。獲取的輸入流中包含了上傳的所有文件,如果 ...
  • 在做介面測試的時候,我們經常會遇到一種情況就是要對介面的參數進行各種可能的校驗,手動修改很麻煩,尤其是那些介面參數有幾十個甚至更多的,有沒有一種方法可以批量的對指定參數做生成處理呢。 答案是肯定的! python的jinja2模板庫可以很好的滿足我們的需求,通過維護一個原始數據模板,將我們想要動態生 ...
  • 1. 獲取輸入框數據wxml中的input上增加bindinput屬性,和方法值在js部分定義與之對應的方法,只要在輸入的時候,數據就會綁定調用到該方法,存入data屬性變數中 2. 調用get請求發起網路請求調用wx.request發起網路請求 3.調用微信Toast介面展示結果 4.按鈕綁定bi ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...