一、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+自定義註解的方式,可以實現前面說的抽取公共非業務模組,對業務邏輯的增強。比如:

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

  自定義註解

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 ============環繞後==============