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