廢話不多說,AOP實戰中必須要懂得API語法。API只是工具,無須死記硬背,收藏整理即可
文章內容已上傳Github: https://github.com/lxchinesszz/spring-learning
一、常用註解
註解 | 說明 |
---|---|
@Before | 前置通知, 在方法執行之前執行 |
@After | 後置通知, 在方法執行之後執行 |
@AfterRunning | 返回通知 在方法返回結果之後執行 |
@AfterThrowing | 異常通知在方法拋出異常之後 |
@Around | 環繞通知, 圍繞着方法執行 |
二、切面表達式
註解 | 說明 |
---|---|
within | 攔截指定類及指定包下所有的類 |
@within | 攔截被指定註解修飾的類 |
this | 攔截指定的類型 |
args | 攔截指定參數類型的方法 |
@annotation | 攔截帶指定註解的方法 |
@args | 攔截方法入參被中@args指定的註解(入參只能有一個) |
execution | 表達式詳情見下文 |
三、API使用案例
1. within
a. API說明
- 精確匹配類名
- 模糊匹配包中所有的類
- 模糊匹配包中所有的帶Impl後綴的
b. 目錄
└── WithinMatchProcessor
├── AopWithinMatchProcessor.java
├── CokeImpl.java
├── Water.java
└── readme.md
c. 攔截代碼
@Aspect
@Component
public class AopWithinMatchProcessor {
/**
* 精確匹配類名
*/
@Pointcut("within(spring.learning.aop.WithinMatchProcessor.Water)")
private void matchClassName() {
}
/**
* 模糊匹配包中所有的類
*/
@Pointcut("within(spring.learning.aop.WithinMatchProcessor.*)")
private void matchAllClassFromPackage() {
}
/**
* 模糊匹配包中所有的帶Impl後綴的
*/
@Pointcut("within(spring.learning.aop.WithinMatchProcessor.*Impl)")
private void matchClassFromPackage() {
}
@Before("matchClassName()")
public void beforeMatchClassName() {
System.out.println("--------精確匹配類名-------");
}
@Before("matchAllClassFromPackage()")
public void beforeMatchAllClassFormPackage() {
System.out.println("--------模糊匹配包中所有的類-------");
}
@Before("matchClassFromPackage()")
public void beforeMatchClassFromPackage() {
System.out.println("--------模糊匹配包中所有的帶Impl後綴的-------");
}
}
2. @within
a. API說明
攔截被指定註解標註的類
b. 目錄
├── AnnotationWithinMatchProcessor
│ ├── AopAnnotationWithinMatchProcessor.java
│ ├── Log.java
│ ├── Sprite.java
│ └── readme.md
c. 攔截代碼
@Log(tag = "SpriteLog")
@Component
public class Sprite {
public void drink() {
System.out.println("空參數");
}
public void drink(Integer age) {
System.out.println("age");
}
public String name() {
return "Sprite.name";
}
public void toCalculate() throws Exception {
System.out.println(0 / 0);
}
}
@Aspect
@Component
public class AopAnnotationWithinMatchProcessor {
/**
* 注意可以將註解,放到參數中,此時@within()會將參數入參名去找到註解的類型
* 凡是被Log標記的類,都會被攔截
*
* @param spriteLog 註解
*/
@Before("@within(spriteLog)")
public void beforeAnnotationMatch(Log spriteLog) {
System.out.println("--------攔截被Log修飾類的所有方法" + spriteLog.tag() + "-------");
}
/**
* 返回值
*
* @param value 返回值
* @param spriteLog 註解
*/
@AfterReturning(value = "@within(spriteLog)", returning = "value")
public void afterReturningAnnotationMatch(String value, Log spriteLog) {
System.out.println("afterReturningAnnotationMatch返回值:" + value + ",註解:" + spriteLog);
}
/**
* 攔截異常
*
* @param e 異常
* @param spriteLog 攔截日誌
*/
@AfterThrowing(value = "@within(spriteLog)", throwing = "e")
public void AfterThrowingAnnotationMatch(Exception e, Log spriteLog) {
System.out.println(e.getMessage());
}
}
3. this
a. API說明
攔截指定的類
b. 目錄
├── ThisMatchProcessor
│ ├── AopThisMatchProcessor.java
│ ├── ThisPerson.java
│ └── readme.md
c. 攔截代碼
@Aspect
@Component
public class AopThisMatchProcessor {
@Before(value = "this(ThisPerson)")
public void thisMatch() {
System.out.println("--------------ThisPerson------------");
}
}
4. args
a. API說明
@Component
public class Person {
public String info(String name) {
return "姓名:" + name;
}
public String info(String name, Integer age) {
return "姓名:" + name + ",年齡:" + age;
}
}
Person類中有兩個info方法,但是入參不一樣,假如要攔截指定入參的方法時候,就可以使用args
b. 目錄
├── ArgsMatchProcessor
│ ├── AopArgsMatchProcessor.java
│ ├── Person.java
│ └── readme.md
c. 攔截代碼
可以看到args 和 within可以通過&&來進行,聯合匹配。另外可以通過returning方法指定方法的返回值。但是注意,類型要和要攔截的方法的返回類型匹配。否則會報錯。
@Aspect
@Component
public class AopArgsMatchProcessor {
@AfterReturning(value = "within(Person) && args(name,age)", returning = "value")
public void beforeArgs(Integer age, String name, String value) {
System.out.println("攔截器邏輯----------------------------");
System.out.println("入參name:" + name);
System.out.println("入參age:" + age);
System.out.println("返回值:" + value);
System.out.println("攔截器邏輯----------------------------");
}
}
5. @annotation
a. API說明
攔截被指定註解標記的方法。
b. 目錄
├── AnnotationMethodMatchProcessor
│ ├── AopAnnotationMethodMatchProcessor.java
│ ├── LogMethod.java
│ └── Main.java
c. 代碼
@Aspect
@Component
public class AopAnnotationMethodMatchProcessor {
@Before(value = "@annotation(logMethod) && args(args)")
public void annotationMethodMatch(LogMethod logMethod, String args) {
System.out.println("註解方法匹配");
}
}
6. @args
a. API說明
攔截方法中入參被@args指定註解的方法。
b. 目錄
├── AnnotationArgsMatchProcessor
│ ├── AopAnnotationArgsMatchProcessor.java
│ ├── Apple.java
│ ├── Fruit.java
│ ├── Orange.java
│ └── Teacher.java
c. 代碼
注意當出現以下異常說明aop聲明的攔截範圍太廣泛了,導致了一些不能攔截的類被攔截從而報錯了,此時只用縮小攔截的範圍即可
Cannot subclass final class org.springframework.boot.autoconfigure.AutoConfigurationPackages$BasePackages
縮小攔截範圍如下使用this攔截指定類型
@Aspect
@Component
public class AopAnnotationArgsMatchProcessor {
@Before(value = "@args(fruit) && this(Teacher)")
public void annotationMethodMatch(Fruit fruit) {
System.out.println("攔截被Fruit+tag:"+fruit.tag());
}
}
7. execution
a. API說明
execution()是最常用的切點函數,其語法如下所示:
execution(<修飾符模式>? <返回類型模式> <方法名模式>(<參數模式>) <異常模式>?) 除了返回類型模式、方法名模式和參數模式外,其它項都是可選的
表達式 | 說明 |
---|---|
execution(public * *(…)) | 匹配所有目標類的public方法 |
execution(* *Test(…)) | 匹配目標類所有以To爲後綴的方法 |
execution(spring.learning.Water.(…)) | 匹配Water接口所有方法 |
execution(spring.learning.Water+.(…)) | 匹配Water接口以及實現類中所有方法(包括Water接口中沒有的方法) |
execution(* spring.learning.*(…)) | 匹配spring.learning包下所有的類所有方法 |
execution(* spring.learning…*(…)) | 匹配spring.learning包及其子孫包下所有的類所有方法 |
execution(* spring…*.Dao.find(…)) | 匹配包名前綴爲spring的任何包下類名後綴爲Dao的方法,方法名必須以find爲前綴 |
execution(* info(String,Integer)) | 匹配info方法中,第一個參數是String,第二個Integer的方法 |
execution(* info(String,*))) | 匹配info方法中,第一個參數是String,第二個任意類型 |
execution(* info(String,…))) | 匹配info方法中,第一個參數是String,後面任意參數 |
execution(* info(Object+))) | 匹配info方法中,方法擁有一個入參,且入參是Object類型或該類的子類 |
最後求關注,求訂閱,感謝您的閱讀,本文由 程序猿升級課 版權所有。如若轉載,請註明出處:程序猿升級課(https://blog.springlearn.cn/)