學習Spring Boot:(二十二)使用 AOP

前言

AOP 1,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。基於AOP實現的功能不會破壞原來程序邏輯,所以它能夠很好的對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。css

正文

Spring Boot 中使用

  1. pom.xml 中加入 aop 依賴:
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
  1. 當咱們須要在非接口上面進行切面操做的時候,就須要 CGLIB來實現 AOP,在系統配置文件中加入設置:
spring: 
  aop:
    proxy-target-class: true

默認爲 falsejava

切點表達式

列出經常使用的幾個表達式:
1. execution() 知足execution中描述的方法簽名。 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
* modifier-pattern:表示方法的修飾符;
* ret-type-pattern:表示方法的返回值
* declaring-type-pattern:表示方法所在的類的路徑
* name-pattern:表示方法名
* param-pattern:表示方法的參數
* throws-pattern:表示方法拋出的異常
* 其中後面跟着?的是可選項。spring

  1. this()是用來限定方法所屬的類,爲接口則限定全部的實現類,爲類的話,限定這單個類。
  2. @annotation表示具備某個標註的方法。
  3. args 表示方法的參數屬於一個特定的類,@args 表示參數有特定的標註註解。
  4. within 包或者類型知足within中描述的包或者類型的類的全部非私有方法,@within 類型擁有@target描述中給出的annotation,其中@target和@within的區別在於@within要求的annotation的級別爲CLASS,而@target爲RUNTIME
    . target 業務實例對象(非代理實例)的類型知足target 中的描述的類型,@target 類型擁有@target描述中給出的annotation
  5. bean() 表示全部匹配的 bean,例如 ,bean(「*Service」),匹配全部 Service 結尾的類。可使用 !bean() 表示不匹配。

注意事項:
* 在各個pattern中,可使用」*」來表示匹配全部。
* 在param-pattern中,能夠指定具體的參數類型,多個參數間用,隔開,各個也能夠用 * 來表示匹配任意類型的參數,如(String)表示匹配一個String參數的方法;(*,String)表示匹配有兩個參數的方法,第一個參數能夠是任意類型,而第二個參數是String類型。
* 能夠用(..)表示零個或多個任意的方法參數。
* 使用&&符號表示與關係,使用||表示或關係、使用!表示非關係。在XML文件中使用andornot這三個符號。編程

AspectJ提供了五種定義通知的標註:springboot

  • @Before:前置通知,在調用目標方法以前執行通知定義的任務
  • @After:後置通知,在目標方法執行結束後,不管執行結果如何都執行通知定義的任務
  • @AfterReturning:後置通知,在目標方法執行結束後,若是執行成功,則執行通知定義的任務
  • @AfterThrowing:異常通知,若是目標方法執行過程當中拋出異常,則執行通知定義的任務
  • @Around:環繞通知,在目標方法執行前和執行後,都須要執行通知定義的任務

經過標註定義通知只須要兩個步驟:
1. 將以上五種標註之一添加到切面的方法中
2. 在標註中設置切點的定義。markdown

建立環繞通知

環繞通知相比其它四種通知有其特殊之處。環繞通知本質上是將前置通知、後置通知和異常通知整合成一個單獨的通知。函數

@Around標註的方法,該方法必須有一個ProceedingJoinPoint類型的參數,spring-boot

在方法體中,須要經過這個參數,以joinPoint.proceed();的形式調用目標方法。注意在環繞通知中必須進行該調用,不然目標方法自己的執行就會被跳過。this

計算方法的執行時間:spa

@Around("logPointCut()") //切點
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //執行方法
        Object result = point.proceed();
        //執行時長(毫秒)
        long time = System.currentTimeMillis() - beginTime;

        return result;
    }

處理通知中參數

獲取參數的方式則須要使用關鍵詞是args

@Pointcut("bean(sysUserServiceImpl) && args(userEntity,..)")
    public void userPointCut(SysUserEntity userEntity) {

    }

    @Before("userPointCut(userEntity)")
    public void validateUser(SysUserEntity userEntity) {
        // to handler args
    }

這裏有個很是嚴格的一點就是,args(userEntity,..),表示目標方法,可能有多個參數,可是包括 userEntity,這裏 userEntity 必須參數名相同,不一樣就編織了。

args()中參數的名稱必須跟切點方法的簽名中public void validateUser(SysUserEntity userEntity)的參數名稱相同。若是使用切點函數定義,其中的參數名稱也必須與通知方法簽名中的參數徹底相同

AfterReturning加強處理

@AfterReturning(pointcut = "logPointCut()", returning = "rtv")
    public void logAfter(Object rtv) {
        System.out.println(Objects.toString(rtv));
    }

使用 @AfterReturning 註解時,指定了一個returning屬性,假設該屬性值爲rvt,這代表容許在Advice方法(logAfter()方法)中定義名爲rvt的形參,程序可經過rvt形參來訪問目標方法的返回值。

注意:
雖然AfterReturning加強處理能夠訪問到方法的返回值,但它不能夠改變目標方法的返回值。

AOP切面的優先級

有時候,咱們對一個方法會有多個切面的問題,這個時候還會涉及到切面的執行順序的問題。

咱們能夠定義每一個切面的優先級, Spring 中提供註解 @Order(i) ,當 i 的值越小,優先級越高。

參考文章


  1. Aspect Oriented Programming的縮寫
相關文章
相關標籤/搜索