Spring Boot學習隨筆- 實現AOP(JoinPoint、ProceedingJoinPoint、自定義注解類實現切面)
第十一章、AOP
11.1 為什么要使用AOP
-
問題
- 現有業務層開發存在問題
- 額外功能代碼存在大量冗余
- 每個方法都需要書寫一遍額外功能代碼不利于項目維護
- 現有業務層開發存在問題
-
Spring中的AOP
AOP:Aspect 切面 + Oriented 面向 Programmaing 面向切面編程
Aspect(切面) = Advice(通知) + Pointcut(切入點)
Advice 通知:業務邏輯中的一些附加操作稱之通知
Pointcut 切入點:配置通知應用于項目中那些業務操作
-
Advice通知就是附加操作的代碼,Advice通知類型都有不同的執行策略和用途。
@Before 在目標方法執行之前執行的通知。它不能阻止方法的執行,但可以在方法執行前添加額外的功能。 @AfterReturning 在目標方法正常返回后執行的通知。例如,如果一個方法正常返回而沒有拋出異常,就會執行這個通知。 @AfterThrowing 在目標方法拋出異常后執行的通知。如果一個方法拋出異常,就會執行這個通知。 @After 在目標方法執行之后執行的通知。無論目標方法如何退出(正常返回或拋出異常),都會執行的通知。 @Around 包圍目標方法的通知,可以在目標方法執行前后添加額外的功能,并決定是否繼續執行目標方法。 -
Pointcut 是切入點,決定了Advice加在哪個具體方法代碼上,具體使用方式:
1.切入點直接寫在附加操作里面 @Around(value="execution(* login(..))") 2.通過@Pointcut注解 聲明切入點,實現復用 @Pointcut("execution(* login(..))") // 復用切入點,解耦合 public void myPointcut(){} @Around("myPointcut()") @After("myPointcut()") @Before("myPointcut()")
11.2 AOP的實現
-
引入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> -
目前UserServiceImp存在的問題
以log日志這種額外功能為例,這樣的重復操作存在冗余代碼和耦合
@Service public class UserServiceImpl implements UserService { @Override public void save(String name) { System.out.println("========log========"); System.out.println("UserServiceImpl.save"); } @Override public void delete(Integer id) { System.out.println("========log========"); System.out.println("UserServiceImpl.delete"); } @Override public void update(String name) { System.out.println("========log========"); System.out.println("UserServiceImpl.update"); } @Override public String find(String name) { System.out.println("========log========"); System.out.println("UserServiceImpl.find"); return "name"; } } -
我們通過一個切面配置類來解耦合
/** * 自定義切面配置類 */ @Configuration // 指定當前類為配置類 @Aspect // 代表這個類是一個切面配置類 public class MyAspectConfig { @Before("execution(* com.example.service.*.*(..))") // @Before代表在業務邏輯執行前運行 value代表切入點 public void before() { System.out.println(" =====前置附加操作:log====== "); } } -
通過切入點,完成整個業務的額外功能覆蓋,運行效果

11.3 JoinPoint 參數詳解
JoinPoint參數可以在通知體內聲明,用于獲取有關方法執行的信息。JoinPoint參數提供了許多有用的方法,例如getSignature()可以獲取方法的簽名,getArgs()可以獲取方法的參數列表,getTarget()可以獲取目標對象等。通過JoinPoint參數,我們可以在通知中訪問和操作方法執行時的上下文信息。
JoinPoint參數通常用于以下幾種情況:
- 記錄日志:獲取方法的簽名和參數列表,從而記錄方法的執行情況,包括方法名、參數值等。
- 異常處理:獲取方法執行時拋出的異常信息,從而進行相應的異常處理。
- 性能監控:獲取方法的執行時間、參數值等信息,用于性能監控和優化。
獲取執行方法的信息
@Before("execution(* com.example.service.*.*(..))") //
public void before(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getName();
Object[] args = joinPoint.getArgs();
System.out.println("Before 執行方法: " + methodName + " 在類:" + className);
System.out.println("方法參數 = " + Arrays.toString(args));
}
11.4 @Around 環繞附加操作
使用@Around注解的方法,參數必須聲明ProceedingJoinPoint,這個參數可以控制目標方法的執行
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("======進入環繞的前置操作======");
System.out.println("當前執行類 = " + pjp.getSignature().getName());
System.out.println("方法名 = " + pjp.getTarget().getClass().getSimpleName());
//放行目標方法執行
Object proceed = pjp.proceed(); // 目標方法繼續執行
System.out.println(" =====進入環繞的后置操作========= ");
return proceed;
}
-
運行效果

11.5 自定義注解方式的切入點表達式@annotation
通過自定義注解@annotation實現切面的好處在于可以使切面的定義更加靈活和可重用。使用自定義注解可以將切面的邏輯和配置信息封裝在注解中,使得切面的使用和配置變得更加簡單和直觀。
-
MyAdvice 自定義注解
@Retention(RetentionPolicy.RUNTIME) // 指定運行時保留 @Target(ElementType.METHOD) //指定修飾 方法 public @interface MyAdvice { } -
放在通知里的@annotation屬性使用,代表只有被
@MyAdvice修飾的方法才會被加入額外功能**@Around("@annotation(com.example.config.MyAdvice)")** public Object around(ProceedingJoinPoint pjp) throws Throwable { } -
通過將注解加在業務邏輯上,實現給目標方法加上額外功能的目的
UserServiceImpl @Override @MyAdvice public String find(String name) { // System.out.println("========log========"); System.out.println("UserServiceImpl.find"); return "name"; }
作者:揚眉劍出鞘
出處: http://www.rzrgm.cn/eyewink/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

浙公網安備 33010602011771號