史上最全AOP切面表達式API使用指南

廢話不多說,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說明

  1. 精確匹配類名
  2. 模糊匹配包中所有的類
  3. 模糊匹配包中所有的帶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/)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章