Spring5(七)——AOP註解

L 發表於 2021-09-15
Spring

一、AOP註解

1、介紹

  上一節介紹了 AspectJ 框架如何實現 AOP,具體的實現方式是通過 xml 來進行配置的。xml 方式思路清晰,便於理解,但是書寫過於麻煩。這一節介紹註解的方式來進行 AOP 配置。

2、案例(註解)

  定義目標物件(被代理的物件)

 1 // 定義一個介面
 2 public interface ITeacher {
 3     void teach();
 4     int add(int i, int j);
 5 }
 6 
 7 // 定義目標物件
 8 @Service
 9 public class Teacher implements ITeacher {
10     @Override
11     public void teach() {
12         System.out.println("老師正在上課");
13     }
14 
15     @Override
16     public int add(int i, int j) {
17         int add = i + j;
18         System.out.println("執行目標方法:老師正在做加法,結果為:" + add);
19         // int throwable = 10 / 0; 測試異常通知
20         return add;
21     }
22 
23     // 目標物件自己的方法,此方法不是介面所以無法代理
24     public void sayHello() {
25         System.out.println("老師會說hello");
26     }
27 
28 }

  編寫一個切面類(通知)

 1 // 建立切面類(包含各種通知)
 2 @Component
 3 @Aspect
 4 public class MyAspect {
 5 
 6     // 1.先定義切入點表示式
 7     @Pointcut("execution(* com.lx.spring.day4.ITeacher.*(..))")
 8     private void myPointcut() {
 9 
10     }
11 
12     // 2.標識此方法為一個前置通知,用來切滿足後面切點表示式的方法
13     @Before("myPointcut()")
14     public void myBefore(JoinPoint joinPoint) {
15         System.out.println("前置通知:方法增強myBefore()" + " , -->" + joinPoint.getSignature().getName());
16     }
17 
18     @AfterReturning(value = "myPointcut()", returning = "object")
19     public void myAfterReturning(JoinPoint joinPoint, Object object) {
20         System.out.println("後置通知:方法增強myAfterReturning()" + " , -->" + joinPoint.getSignature().getName() + " , -->" + object);
21     }
22 
23     public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
24         System.out.println("============環繞前==============");
25         Object obj = joinPoint.proceed(); // 手動執行目標方法
26         System.out.println("============環繞後==============");
27         return obj;
28     }
29 
30     public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
31         System.out.println("丟擲異常通知:" + e.getMessage());
32     }
33 
34     public void myAfter() {
35         System.out.println("最終通知:方法增強myAfter()");
36     }
37 
38 }

  編寫配置檔案 application.xml

1 <!-- 1.自動掃描(自動注入bean) -->
2 <context:component-scan base-package="com.lx.spring.day4"/>
3 
4 <!-- 2.掃描 @Aspect 告訴 spring 這是一個切面類 -->
5 <aop:aspectj-autoproxy/>
 1 // 測試類
 2 public class Main {
 3     public static void main(String[] args) {
 4         ApplicationContext app = new ClassPathXmlApplicationContext("app4.xml");
 5         ITeacher iTeacher = app.getBean(ITeacher.class);
 6 
 7         iTeacher.add(11, 24);
 8     }
 9 }
10 
11 // 結果
12 前置通知:方法增強myBefore() , -->add
13 執行目標方法:老師正在做加法,結果為:35
14 後置通知:方法增強myAfterReturning() , -->add , -->35

  說明:對比 xml 的配置,不難理解註解的方式。

  @Service @Component
  <context:component-scan base-package="com.lx.spring.day4"/>

  用於 Spring 掃描並註冊bean。

  @Aspect:指明這是一個切面類
  <aop:aspectj-autoproxy/>:開啟切面註解掃描

3、優先順序

  有多個增強類對同一個方法進行增強,設定增強類優先順序,在增強類上面新增註解 @Order(數字型別值),數字型別值越小優先順序越高。

1 @Component
2 @Aspect
3 @Order(1)
4 public class MyAspect2 {}

  優先順序:這裡的優先順序,只會影響兩個增強類對應的方法,執行的先後順序。並不會只執行優先順序高的。

二、AOP+自定義註解

  通過AOP+自定義註解的方式,可以實現前面說的抽取公共非業務模組,對業務邏輯的增強。比如:

  需求:①想要對業務邏輯層的所有方法,列印出入參和出參,做日誌管理。②對業務邏輯層的方法入口,開啟事務,邏輯執行後,提交事務,等。

Spring5(七)——AOP註解

  自定義註解

1 // 用於日誌列印
2 @Target({ElementType.METHOD})
3 @Retention(RetentionPolicy.RUNTIME)
4 @Documented
5 public @interface Log {
6 
7     String value() default "";
8 }
  編寫切面類(通知)
 1 @Component
 2 @Aspect
 3 public class MyAspect {
 4 
 5     // 定義切入點為 有註解Log的方法
 6     @Pointcut("@annotation(com.lx.spring.day5.Log) ")
 7     private void myLogPointcut() {
 8 
 9     }
10     
11     // 為切入點增強一個環繞通知,可以在這裡寫列印入參出參的邏輯
12     @Around("myLogPointcut()")
13     public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
14         System.out.println("============環繞前==============");
15         Object obj = joinPoint.proceed(); // 手動執行目標方法
16         System.out.println("============環繞後==============");
17         return obj;
18     }
19 
20 }
  在相應的方法上加註解
 1 @Service
 2 public class Teacher implements ITeacher {
 3 
 4     // 為需要列印入參出參的方法 加上@Log註解即可
 5     @Log
 6     @Override
 7     public int add(int i, int j) {
 8         int add = i + j;
 9         System.out.println("執行目標方法:老師正在做加法,結果為:" + add);
10         // int throwable = 10 / 0; 測試異常通知
11         return add;
12     }
13 
14 }
  測試類
 1 public class Main {
 2     public static void main(String[] args) {
 3         ApplicationContext app = new ClassPathXmlApplicationContext("app4.xml");
 4         ITeacher iTeacher = app.getBean(ITeacher.class);
 5 
 6         iTeacher.add(11, 24);
 7     }
 8 }
 9 
10 // 結果
11 ============環繞前==============
12 執行目標方法:老師正在做加法,結果為:35
13 ============環繞後==============