Spring 面向切面編程(AOP)

一、aop介紹

(一)前言

一般的后端開發流程是縱向開發,就是controller(控制層)->service(業務層)->mapper(數據持久層),Spring采用動態代理技術可以在程序的運行過程中對每一層進行增強,也就是面向切面編程(Aspect Oriented Programming)。
在這里插入圖片描述

(二)使用場景

(1)想要在每類業務處理的時候保存日志,包含業務處理的具體數據、具體時間、具體用戶;
(2)如果業務方法是異步的,可以監控業務方法是否報錯;
(3)可以實現數據庫事務;

(三)相關術語

1、Joinpoint(連接點)

類里面可以被增強的方法即是連接點;

2、Pointcut(切入點)

對連接點進行攔截的定義即是切入點。
切入點的實現方式有兩種:
(1)使用@Pointcut注解定義切入點;
(2)自定義注解作為連接點,對注解進行攔截;

3、Advice(通知)

攔截到切入點之后要做的事情
通知分為以下幾類:
(1)前置通知(@Before):目標方法執行之前執行;
(2)后置通知(@After):目標方法執行之后執行,無論連接點是否出現異常,都會執行;
(3)異常通知(@AfterThrowing):連接點出現異常后才會執行;
(4)返回通知(@AfterReturning):連接點成功執行后,執行返回通知方法,如果連接點方法出現異常,該通知不執行;
(5)環繞通知(@Around):以上四種通知可以通過環繞通知實現;

4、Aspect(切面)

切入點和通知的結合

5、Target(目標對象)

要增強的類

二、代碼實現

(一)準備工作

1、控制層(Controller)
package com.xiaobai.aroundtest.controller;import com.xiaobai.aroundtest.entity.Article;
import com.xiaobai.aroundtest.service.IAroundTestService;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @author wangtw* @date 2023/12/6 0:20* @description 切面測試控制器*/
@AllArgsConstructor
@RestController
public class AroundTestController {private final IAroundTestService aroundTestService;@PostMapping("aroundTest")public void aroundTest(@RequestParam String name, @RequestBody Article article) {aroundTestService.aroundTest(name, article);}
}
2、業務層(Service)

(1)業務層接口

package com.xiaobai.aroundtest.service;import com.xiaobai.aroundtest.annotation.AroundTest;
import com.xiaobai.aroundtest.entity.Article;/*** @author wangtw* @date 2023/12/6 0:39* @description 環繞通知測試服務類*/
public interface IAroundTestService {void aroundTest(String name, Article article);
}

(2)業務層實現類

package com.xiaobai.aroundtest.service.impl;import com.xiaobai.aroundtest.annotation.AroundTest;
import com.xiaobai.aroundtest.entity.Article;
import com.xiaobai.aroundtest.service.IAroundTestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;/*** @author wangtw* @date 2023/12/6 0:42* @description*/
@Slf4j
@Service
public class AroundTestServiceImpl implements IAroundTestService {@AroundTest // 自定義注解@Overridepublic void aroundTest(String name, Article article) {log.info("保存文章");}
}

(二)使用@Pointcut定義切入點

1、前言

@Pointcut 可以使用 annotation、within、execution 等方式將 方法(method)、類(class)、接口(interface)、包(package) 等作為切入點,
以下代碼使用的是execution指定切入點。

2、execution語法
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

(1)修飾符匹配(modifier-pattern?)
(2)返回值匹配(ret-type-pattern)可以為*表示任何返回值,全路徑的類名等
(3)類路徑匹配(declaring-type-pattern?)
(4)方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set開頭的所有方法
(5)參數匹配((param-pattern))可以指定具體的參數類型,多個參數間用“,”隔開,各個參數也可以用“”來表示匹配任意類型的參數,如(String)表示匹配一個String參數的方法;(,String) 表示匹配有兩個參數的方法,第一個參數可以是任意類型,而第二個參數是String類型;可以用(…)表示零個或多個任意參數
(6)異常類型匹配(throws-pattern?)
其中后面跟著“?”的是可選項

3、定義切面

以下表達式指定的是com.xiaobai及其子包下service.impl的所有方法

execution(* com.xiaobai..*.service.impl..*.*(..))

切面類需要使用@Aspect指定,使用@Component注解將切面交由Spring容器管理,切面類代碼如下:

package com.xiaobai.aroundtest.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;/*** @author wangtw* @date 2023/12/6 0:47* @description service切面*/
@Aspect
@Component
@Slf4j
public class TransactionalAspect {@Pointcut("execution(* com.xiaobai..*.service.impl..*.*(..))")private void pointCut() {}/*** 開啟事務(前置通知)*/@Before("pointCut()")public  void beginTransaction(){try {log.info("開啟事務");}catch (Exception e){e.printStackTrace();}}/*** 提交事務(后置返回通知)*/@AfterReturning("pointCut()")public  void commit(){try {log.info("提交事務");}catch (Exception e){e.printStackTrace();}}/*** 回滾事務(異常通知)*/@AfterThrowing("pointCut()")public  void rollback(){try {log.info("回滾事務");}catch (Exception e){e.printStackTrace();}}/*** 釋放連接(后置通知)*/@After("pointCut()")public  void release(){try {log.info("釋放連接");}catch (Exception e){e.printStackTrace();}}
}

(三)通過攔截注解的方式實現切面

1、自定義注解

(1)使用@Target指定目標元素,這個注解是用于攔截方法,使用ElementType.METHOD
(2)使用@Retention指定注解的生命周期:
RetentionPolicy.RUNTIME 表示此注解被保存到class文件中,jvm加載class文件后,此注解仍存在;RetentionPolicy.SOURCE 表示此注解只會保留到源文件中,文件編譯成class文件后,此注解就會消失;RetentionPolicy.CLASS 表示此注解會被保存到class文件中,但jvm加載class文件后,此注解會被遺棄;

package com.xiaobai.aroundtest.annotation;import java.lang.annotation.*;/*** @author wangtw* @date 2023/12/6 0:45* @description*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AroundTest {/*** 實體類屬性名* @return*/String name() default "name";
}
2、定義切面

@Around 用于實現環繞通知,使用 @annotation(aroundTest) 指定需要攔截的注解,環繞通知需要攜帶 ProceedingJoinPoint 類型的參數。
可以使用 (MethodSignature) ProceedingJoinPoint.getSignature() 獲取方法參數,具體代碼如下:

// 獲取參數名稱
MethodSignature methodSignature = (MethodSignature) point.getSignature();
String[] parameterNames = methodSignature.getParameterNames();

使用 ProceedingJoinPoint.getArgs() 方法獲取參數值

// 獲取參數
Object[] args = point.getArgs();

切面代碼如下:

package com.xiaobai.aroundtest.aspect;import com.xiaobai.aroundtest.annotation.AroundTest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;/*** @author wangtw* @date 2023/12/6 0:47* @description*/
@Aspect
@Component
@Slf4j
public class AroundTestAspect {@Around("@annotation(aroundTest)")public Object around(ProceedingJoinPoint point, AroundTest aroundTest) {// 獲取參數名稱MethodSignature methodSignature = (MethodSignature) point.getSignature();String[] parameterNames = methodSignature.getParameterNames();// 獲取參數Object[] args = point.getArgs();for (int i = 0; i < parameterNames.length; i++) {if (parameterNames[i].equals(aroundTest.name())) {log.info("保存{}為{}的文章", parameterNames[i], args[i]);}}String info = "";for (Object arg : args) {if (arg instanceof String) {} else {// 獲取文章信息PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(arg.getClass());for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {if (!String.class.isAssignableFrom(propertyDescriptor.getPropertyType())) {continue;}Method readMethod = propertyDescriptor.getReadMethod();if (readMethod == null) {continue;}Object propertyValue = null;try {propertyValue = readMethod.invoke(arg, null);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}info = info.concat(propertyDescriptor.getName()).concat(":").concat(propertyValue == null ? "" : propertyValue.toString()).concat(",");}}}Object result = null;try {result = point.proceed(point.getArgs());} catch (Throwable e) {e.printStackTrace();log.error("方法異常,文章信息:{}", info);}log.info("修改成功, 文章信息:{}", info);return result;}
}

三、總結

1、Spring使用Cglib增強了service,注入到controller的service實際上是個代理對象
在這里插入圖片描述

2、使用postman測試controller接口,輸出結果如下:
在這里插入圖片描述

保存name為java核心技術的文章
開啟事務
保存文章
提交事務
釋放連接
修改成功, 文章信息:description:囊括了Java平臺標準版(JavaSE/J2SE)的全部基礎知識,提供了大量完整且具有實際意義的應用實例,詳細介紹了Java語言基礎知識、面向對象程序設計、接口與內部類、事件監聽器模型、swing圖形用戶界面程序設計、打包應用程序、異常處理、登錄與調試、泛型程序設計、集合框架、多線程等內容,name:java核心技術,

參考

SpringBoot+Vue全棧開發實戰 王松 清華大學出版社
@Retention注解詳解 自由的棉花
Spring AOP中@Pointcut切入點表達式使用介紹 Roc Lau
【Spring AOP】@Aspect結合案例詳解(一): @Pointcut使用@annotation + 五種通知Advice注解(已附源碼) 天罡gg
AspectJ 切面注解中五種通知注解:@Before、@After、@AfterReturning、@AfterThrowing、@Around 蒼鷹蛟龍
Spring——面向切面編程(AOP) 行者無疆_ty

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/212774.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/212774.shtml
英文地址,請注明出處:http://en.pswp.cn/news/212774.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Wireshark添加自定義協議解析

最終效果如下&#xff1a; 參考文檔&#xff1a;https://mika-s.github.io/topics/ 此參考文檔中7個例子教我們如何編寫lua腳本去識別我們自定義的協議 安裝Wireshark https://www.wireshark.org/上下載安裝包安裝即可。我的安裝路徑是D:\Install\Wireshark&#xff0c;在W…

Leetcode—389.找不同【簡單】

2023每日刷題&#xff08;五十五&#xff09; Leetcode—389.找不同 實現代碼 char findTheDifference(char* s, char* t) {int len strlen(s);int len2 len 1;int a[26] {0};int b[26] {0};if(len 0) {return t[0];}for(int i 0; i < len; i) {int idx s[i] - a;…

HTML的img常見應用屬性

1、src&#xff1a;指定圖像的URL&#xff0c;即圖像的路徑。 2、alt&#xff1a;指定圖像的替代文本&#xff0c;當圖像無法顯示時&#xff0c;會顯示替代文本。 3、width&#xff1a;指定圖像的寬度&#xff0c;可以使用像素值或百分比。 4、height&#xff1a;指定圖像的…

【設計模式--創建型--建造者模式】

建造者模式 建造者模式概述結構結果優缺點使用場景 將上述案例改為鏈式調用結果 建造者模式 概述 將一個復雜對象的構建與表示分離&#xff0c;使得同樣的構建過程可以創建不同的表示。 分離了部件的構建&#xff08;由Builder來負責&#xff09;和裝配&#xff08;由Direct…

辦公word-從不是第一頁添加頁碼

總結 實際需要注意的是&#xff0c;分隔符、分節符和分頁符并不是一個含義 分隔符包含其他兩個&#xff1b;分頁符&#xff1a;是增加一頁&#xff1b;分節符&#xff1a;指將文檔分為幾部分。 從不是第一頁插入頁碼1步驟 1&#xff0c;插入默認頁碼 自己可以測試時通過**…

win11 powershell conda 激活環境后不顯示環境名稱

win11 powershell conda 激活環境后不顯示環境名稱 問題現象解決方法 問題現象 安裝 Anaconda 后在 powershell 中激活環境后&#xff0c;命令行前面不顯示環境名稱 解決方法 在 powershell 中執行 conda init 重新打開 poweshell 出現以下問題&#xff0c;請參考 win11 p…

華為OD機試真題-5G網絡建設-2023年OD統一考試(C卷)

題目描述: 現需要在某城市進行5G網絡建設,已經選取N個地點設置5G基站,編號固定為1到N,接下來需要各個基站之間使用光纖進行連接以確保基站能互聯互通,不同基站之間架設光纖的成本各不相同,且有些節點之間已經存在光纖相連,請你設計算法,計算出能聯通這些基站的最小成本…

HarmonyOS鴻蒙應用開發——HTTP網絡訪問與封裝

文章目錄 基本使用封裝參考 基本使用 鴻蒙應用發起HTTP請求的基本使用&#xff0c;如下&#xff1a; 導入http模塊創建httpRequest對象發起http請求&#xff0c;并處理響應結果 第一、導入http模塊&#xff1a; import http from ohos.net.http第二、創建httpRequest對象&a…

二分查找|滑動窗口|前綴和|LeetCode209: 長度最小的子數組

長度最短的子數組 作者推薦 【動態規劃】【廣度優先】LeetCode2258:逃離火災 本文涉及的基礎知識點 二分查找算法合集 C算法&#xff1a;前綴和、前綴乘積、前綴異或的原理、源碼及測試用例 包括課程視頻 滑動窗口 題目 給定一個含有 n 個正整數的數組和一個正整數 target…

facebook回傳

1、引入依賴 首先引入依賴&#xff0c;這里我使用API v14.0&#xff1a; <dependency><groupId>com.facebook.business.sdk</groupId><artifactId>facebook-java-business-sdk</artifactId><version>14.0.0</version></dependen…

在IDEA中創建Maven項目時沒有src文件、不自動配置文件

錯誤示例&#xff1a; 沒有src文件&#xff0c;并且沒有自動下載相關的配置文件 對我這中情況無效的解決辦法&#xff1a; ①配置好下列圖中圈出來的文件 ②在VM選項中輸入&#xff1a;“-DarchetypeInternal” ③點擊應用&#xff0c;再點擊確定 ④還是不行 解決辦法&#x…

GridBagLayout GridBagConstraints 筆記231130

實例化使用模板 GridBagLayout gbl new GridBagLayout(); // gbl.columnWidths new int[]{200,200,200}; // 用數組設置列 // gbl.rowHeights new int[]{100,100,100,100,100}; // 用數組設置行GridBagConstraints gbc new GridBagConstraints();/*** gridBagConstrain…

14-1、IO流

14-1、IO流 lO流打開和關閉lO流打開模式lO流對象的狀態 非格式化IO二進制IO讀取二進制數據獲取讀長度寫入二進制數據 讀寫指針 和 隨機訪問設置讀/寫指針位置獲取讀/寫指針位置 字符串流 lO流打開和關閉 通過構造函數打開I/O流 其中filename表示文件路徑&#xff0c;mode表示打…

用Guava做本地緩存示例

緩存的作用 提升系統性能&#xff0c;暫時在內存中保存業務系統的數據處理結果&#xff0c;并且等待下次訪問使用 本地緩存和分布式緩存 緩存分為本地緩存與分布式緩存。本地緩存為了保證線程安全問題&#xff0c;一般使用ConcurrentMap的方式保存在內存之中&#xff0c;而常…

【KCC@南京】KCC南京“數字經濟-開源行”活動回顧錄

11月26日&#xff0c;由KCC南京、中科南京軟件研究所、傲空間、PowerData聯合主辦的 KCC南京“數字經濟-開源行” 的活動已圓滿結束。此次活動&#xff0c;3 場主題研討&#xff0c;11 場分享&#xff0c;現場參會人數 60&#xff0c;線上直播觀看 3000&#xff0c;各地小伙伴從…

Android畫布Canvas繪圖scale,Kotlin

Android畫布Canvas繪圖scale&#xff0c;Kotlin <?xml version"1.0" encoding"utf-8"?> <androidx.appcompat.widget.LinearLayoutCompat xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.…

數據倉庫工具Hive

1. 請解釋Hive是什么&#xff0c;它的主要用途是什么&#xff1f; Hive是一個基于Hadoop的數據倉庫工具&#xff0c;主要用于處理和分析大規模結構化數據。它可以將結構化的數據文件映射為一張數據庫表&#xff0c;并提供類似SQL的查詢功能&#xff0c;將SQL語句轉換為MapRedu…

Windows 和 MacOS 上安裝配置ADB(安卓調試橋)

一、Android 調試橋 (ADB) Android 調試橋&#xff08;ADB&#xff09; 是一款多功能命令行工具&#xff0c;它讓你能夠更便捷地訪問和管理 Android 設備。使用 ADB 命令&#xff0c;你可以輕松執行以下操作 在設備上安裝、復制和刪除文件&#xff1b;安裝應用程序&#xff1…

YOLOV3 SPP 目標檢測項目(針對xml或者yolo標注的自定義數據集)

1. 目標檢測的兩種標注形式 項目下載地址:YOLOV3 SPP網絡對自定義數據集的目標檢測(標注方式包括xml或者yolo格式) 目標檢測邊界框的表現形式有兩種: YOLO(txt) : 第一個為類別,后面四個為邊界框,x,y中心點坐標以及h,w的相對值 xml文件:類似于網頁的標注文件,里面會…

力扣第 375 場周賽(Java)

文章目錄 T1 統計已測試設備代碼解釋 T2 雙模冪運算代碼解釋 T3 統計最大元素出現至少 K 次的子數組代碼解釋 T4 統計好分割方案的數目代碼解釋 鏈接&#xff1a;第 375 場周賽 - 力扣&#xff08;LeetCode&#xff09; T1 統計已測試設備 給你一個長度為 n 、下標從 0 開始的…