網站首頁 編程語言 正文
目錄
1.SpringAOP底層技術組成
2.初步實現
1)添加依賴
2)準備接口
3)純凈實現類
4)聲明切面類
5)開啟aspectj注解支持
6)測試效果
3.獲取通知細節信息
1)JoinPoint接口
4.切點表達式語法
5.重用/提取切點表達式
1)重用切點表達式的優點
2)同一類內部引用
3)不同類中引用
6.環繞通知
7.切面優先級設置
小結
1.SpringAOP底層技術組成
- 動態代理(InvocationHandler):JDK原生的實現方式,需要被代理的目標類必須實現接口。因為這個技術要求代理對象和目標對象實現同樣的接口(兄弟兩個拜把子模式)。
- cglib:通過繼承被代理的目標類(認干爹模式)實現代理,所以不需要目標類實現接口。
- AspectJ:早期的AOP實現的框架,SpringAOP借用了AspectJ中的AOP注解。
2.初步實現
1)添加依賴
<!-- spring-aspects會幫我們傳遞過來aspectjweaver -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.6</version>
</dependency>
2)準備接口
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
3)純凈實現類
package com.atguigu.proxy;
/**
* 實現計算接口,單純添加 + - * / 實現! 摻雜其他功能!
*/
@Component
public class CalculatorPureImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
return result;
}
}
4)聲明切面類
package com.atguigu.advice;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
// @Aspect表示這個類是一個切面類
@Aspect
// @Component注解保證這個切面類能夠放入IOC容器
@Component
public class LogAspect {
// @Before注解:聲明當前方法是前置通知方法
// value屬性:指定切入點表達式,由切入點表達式控制當前通知方法要作用在哪一個目標方法上
@Before(value = "execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))")
public void printLogBeforeCore() {
System.out.println("[AOP前置通知] 方法開始了");
}
@AfterReturning(value = "execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))")
public void printLogAfterSuccess() {
System.out.println("[AOP返回通知] 方法成功返回了");
}
@AfterThrowing(value = "execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))")
public void printLogAfterException() {
System.out.println("[AOP異常通知] 方法拋異常了");
}
@After(value = "execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))")
public void printLogFinallyEnd() {
System.out.println("[AOP后置通知] 方法最終結束了");
}
}
5)開啟aspectj注解支持
a.xml方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 進行包掃描-->
<context:component-scan base-package="com.atguigu" />
<!-- 開啟aspectj框架注解支持-->
<aop:aspectj-autoproxy />
</beans>
b.配置類方式
@Configuration
@ComponentScan(basePackages = "com.atguigu")
//作用等于 <aop:aspectj-autoproxy /> 配置類上開啟 Aspectj注解支持!
@EnableAspectJAutoProxy
public class MyConfig {
}
6)測試效果
//@SpringJUnitConfig(locations = "classpath:spring-aop.xml")
@SpringJUnitConfig(value = {MyConfig.class})
public class AopTest {
@Autowired
private Calculator calculator;
@Test
public void testCalculator(){
calculator.add(1,1);
}
}
輸出結果
"C:\Program Files\Java\jdk-17\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\lib\idea_rt.jar=65511:D:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Users\Jackiechan\.m2\repository\org\junit\platform\junit-platform-launcher\1.3.1\junit-platform-launcher-1.3.1.jar;C:\Users\Jackiechan\.m2\repository\org\apiguardian\apiguardian-api\1.0.0\apiguardian-api-1.0.0.jar;C:\Users\Jackiechan\.m2\repository\org\junit\platform\junit-platform-engine\1.3.1\junit-platform-engine-1.3.1.jar;C:\Users\Jackiechan\.m2\repository\org\junit\platform\junit-platform-commons\1.3.1\junit-platform-commons-1.3.1.jar;C:\Users\Jackiechan\.m2\repository\org\opentest4j\opentest4j\1.1.1\opentest4j-1.1.1.jar;C:\Users\Jackiechan\.m2\repository\org\junit\jupiter\junit-jupiter-engine\5.3.1\junit-jupiter-engine-5.3.1.jar;C:\Users\Jackiechan\.m2\repository\org\junit\jupiter\junit-jupiter-api\5.3.1\junit-jupiter-api-5.3.1.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\plugins\junit\lib\junit5-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\plugins\junit\lib\junit-rt.jar;D:\javaprojects\backend-engineering\part01-spring\spring-aop-annotation\target\test-classes;D:\javaprojects\backend-engineering\part01-spring\spring-aop-annotation\target\classes;D:\repository\org\springframework\spring-context\6.0.6\spring-context-6.0.6.jar;D:\repository\org\springframework\spring-beans\6.0.6\spring-beans-6.0.6.jar;D:\repository\org\springframework\spring-core\6.0.6\spring-core-6.0.6.jar;D:\repository\org\springframework\spring-jcl\6.0.6\spring-jcl-6.0.6.jar;D:\repository\org\springframework\spring-expression\6.0.6\spring-expression-6.0.6.jar;D:\repository\org\junit\jupiter\junit-jupiter-api\5.3.1\junit-jupiter-api-5.3.1.jar;D:\repository\org\apiguardian\apiguardian-api\1.0.0\apiguardian-api-1.0.0.jar;D:\repository\org\opentest4j\opentest4j\1.1.1\opentest4j-1.1.1.jar;D:\repository\org\junit\platform\junit-platform-commons\1.3.1\junit-platform-commons-1.3.1.jar;D:\repository\org\springframework\spring-test\6.0.6\spring-test-6.0.6.jar;D:\repository\jakarta\annotation\jakarta.annotation-api\2.1.1\jakarta.annotation-api-2.1.1.jar;D:\repository\mysql\mysql-connector-java\8.0.25\mysql-connector-java-8.0.25.jar;D:\repository\com\google\protobuf\protobuf-java\3.11.4\protobuf-java-3.11.4.jar;D:\repository\com\alibaba\druid\1.2.8\druid-1.2.8.jar;D:\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;D:\repository\org\springframework\spring-aop\6.0.6\spring-aop-6.0.6.jar;D:\repository\org\springframework\spring-aspects\6.0.6\spring-aspects-6.0.6.jar;D:\repository\org\aspectj\aspectjweaver\1.9.9.1\aspectjweaver-1.9.9.1.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5 com.atguigu.test.AopTest,testCalculator
[AOP前置通知] 方法開始了
[AOP返回通知] 方法成功返回了
[AOP后置通知] 方法最終結束了
3.獲取通知細節信息
1)JoinPoint接口
需要獲取方法簽名、傳入的實參等信息時,可以在通知方法聲明JoinPoint類型的形參。
- 要點1:JoinPoint 接口通過 getSignature() 方法獲取目標方法的簽名(方法聲明時的完整信息)
- 要點2:通過目標方法簽名對象獲取方法名
- 要點3:通過 JoinPoint 對象獲取外界調用目標方法時傳入的實參列表組成的數組
// @Before注解標記前置通知方法
// value屬性:切入點表達式,告訴Spring當前通知方法要套用到哪個目標方法上
// 在前置通知方法形參位置聲明一個JoinPoint類型的參數,Spring就會將這個對象傳入
// 根據JoinPoint對象就可以獲取目標方法名稱、實際參數列表
@Before(value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))")
public void printLogBeforeCore(JoinPoint joinPoint) {
// 1.通過JoinPoint對象獲取目標方法簽名對象
// 方法的簽名:一個方法的全部聲明信息
Signature signature = joinPoint.getSignature();
// 2.通過方法的簽名對象獲取目標方法的詳細信息
String methodName = signature.getName();
System.out.println("methodName = " + methodName);
int modifiers = signature.getModifiers();
System.out.println("modifiers = " + modifiers);
String declaringTypeName = signature.getDeclaringTypeName();
System.out.println("declaringTypeName = " + declaringTypeName);
// 3.通過JoinPoint對象獲取外界調用目標方法時傳入的實參列表
Object[] args = joinPoint.getArgs();
// 4.由于數組直接打印看不到具體數據,所以轉換為List集合
List<Object> argList = Arrays.asList(args);
System.out.println("[AOP前置通知] " + methodName + "方法開始了,參數列表:" + argList);
}
2)方法返回值
在返回通知中,通過@AfterReturning注解的returning屬性獲取目標方法的返回值!
// @AfterReturning注解標記返回通知方法
// 在返回通知中獲取目標方法返回值分兩步:
// 第一步:在@AfterReturning注解中通過returning屬性設置一個名稱
// 第二步:使用returning屬性設置的名稱在通知方法中聲明一個對應的形參
@AfterReturning(
value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))",
returning = "targetMethodReturnValue"
)
public void printLogAfterCoreSuccess(JoinPoint joinPoint, Object targetMethodReturnValue) {
String methodName = joinPoint.getSignature().getName();
System.out.println("[AOP返回通知] "+methodName+"方法成功結束了,返回值是:" + targetMethodReturnValue);
}
3)異常對象捕捉
在異常通知中,通過@AfterThrowing注解的throwing屬性獲取目標方法拋出的異常對象
// @AfterThrowing注解標記異常通知方法
// 在異常通知中獲取目標方法拋出的異常分兩步:
// 第一步:在@AfterThrowing注解中聲明一個throwing屬性設定形參名稱
// 第二步:使用throwing屬性指定的名稱在通知方法聲明形參,Spring會將目標方法拋出的異常對象從這里傳給我們
@AfterThrowing(
value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))",
throwing = "targetMethodException"
)
public void printLogAfterCoreException(JoinPoint joinPoint, Throwable targetMethodException) {
String methodName = joinPoint.getSignature().getName();
System.out.println("[AOP異常通知] "+methodName+"方法拋異常了,異常類型是:" + targetMethodException.getClass().getName());
}
4.切點表達式語法
1)切點表達式作用
AOP切點表達式(Pointcut Expression)是一種用于指定切點的語言,它可以通過定義匹配規則,來選擇需要被切入的目標對象。
2)切點表達式語法
3)切點表達式案例
1.查詢某包某類下,訪問修飾符是公有,返回值是int的全部方法
public int xx.xx.jj.*()
2.查詢某包下類中第一個參數是String的方法
* xx.xx.jj.*(String..)
3.查詢全部包下,無參數的方法!
* *..*.*()
4.查詢com包下,以int參數類型結尾的方法
* com..*.*(..int)
5.查詢指定包下,Service開頭類的私有返回值int的無參數方法
private int xx.xx.Service*.*()
5.重用/提取切點表達式
1)重用切點表達式的優點
之前編寫切點表達式時,發現, 許多增強方法的切點表達式相同!
出現了冗余,如果需要切換也不方便統一維護!
我們可以將切點提取,在增強上進行引用即可!
2)同一類內部引用
提取
// 切入點表達式重用
@Pointcut("execution(public int com.atguigu.aop.api.Calculator.add(int,int)))")
public void declarPointCut() {}
注意:提取切點注解使用@Pointcut(切點表達式) , 需要添加到一個無參數無返回值方法上即可!
引用
@Before(value = "declarPointCut()")
public void printLogBeforeCoreOperation(JoinPoint joinPoint) {
3)不同類中引用
不同類在引用切點,只需要添加類的全限定符+方法名即可!
@Before(value = "com.atguigu.spring.aop.aspect.LogAspect.declarPointCut()")
public Object roundAdvice(ProceedingJoinPoint joinPoint) {
4)切點統一管理
建議:將切點表達式統一存儲到一個類中進行集中管理和維護!
@Component
public class AtguiguPointCut {
@Pointcut(value = "execution(public int *..Calculator.sub(int,int))")
public void atguiguGlobalPointCut(){}
@Pointcut(value = "execution(public int *..Calculator.add(int,int))")
public void atguiguSecondPointCut(){}
@Pointcut(value = "execution(* *..*Service.*(..))")
public void transactionPointCut(){}
}
6.環繞通知
環繞通知對應整個 try...catch...finally 結構,包括前面四種通知的所有功能。
// 使用@Around注解標明環繞通知方法
@Around(value = "com.atguigu.aop.aspect.AtguiguPointCut.transactionPointCut()")
public Object manageTransaction(
// 通過在通知方法形參位置聲明ProceedingJoinPoint類型的形參,
// Spring會將這個類型的對象傳給我們
ProceedingJoinPoint joinPoint) {
// 通過ProceedingJoinPoint對象獲取外界調用目標方法時傳入的實參數組
Object[] args = joinPoint.getArgs();
// 通過ProceedingJoinPoint對象獲取目標方法的簽名對象
Signature signature = joinPoint.getSignature();
// 通過簽名對象獲取目標方法的方法名
String methodName = signature.getName();
// 聲明變量用來存儲目標方法的返回值
Object targetMethodReturnValue = null;
try {
// 在目標方法執行前:開啟事務(模擬)
log.debug("[AOP 環繞通知] 開啟事務,方法名:" + methodName + ",參數列表:" + Arrays.asList(args));
// 過ProceedingJoinPoint對象調用目標方法
// 目標方法的返回值一定要返回給外界調用者
targetMethodReturnValue = joinPoint.proceed(args);
// 在目標方法成功返回后:提交事務(模擬)
log.debug("[AOP 環繞通知] 提交事務,方法名:" + methodName + ",方法返回值:" + targetMethodReturnValue);
}catch (Throwable e){
// 在目標方法拋異常后:回滾事務(模擬)
log.debug("[AOP 環繞通知] 回滾事務,方法名:" + methodName + ",異常:" + e.getClass().getName());
}finally {
// 在目標方法最終結束后:釋放數據庫連接
log.debug("[AOP 環繞通知] 釋放數據庫連接,方法名:" + methodName);
}
return targetMethodReturnValue;
}
7.切面優先級設置
相同目標方法上同時存在多個切面時,切面的優先級控制切面的內外嵌套順序。
- 優先級高的切面:外面
- 優先級低的切面:里面
使用 @Order 注解可以控制切面的優先級:
- @Order(較小的數):優先級高
- @Order(較大的數):優先級低
小結
a. ?如果目標類有接口,選擇使用jdk動態代理
b. ?如果目標類沒有接口,選擇cglib動態代理
c. ?如果有接口,接口接值
d. ?如果沒有接口,類進行接值
原文鏈接:https://blog.csdn.net/weixin_69134926/article/details/136462079
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-08-25 R語言行篩選的方法之filter函數詳解_R語言
- 2022-10-03 Docker啟動失敗報錯Failed?to?start?Docker?Application?Con
- 2022-11-18 Python正則表達式re.sub()用法詳解_python
- 2021-12-10 k8s部署ingress-nginx的方法步驟_nginx
- 2022-06-21 C語言分別實現棧和隊列詳解流程_C 語言
- 2022-10-22 Kotlin線程的橋接與切換使用介紹_Android
- 2022-06-22 Python?裝飾器常用的創建方式及源碼示例解析_python
- 2022-05-05 Python學習之集合的常用方法總結_python
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支