Spring AOP 應用篇

添加依賴

使用Spring AOP必須添加AOP的依賴包,並配置AOPjava

  • Spring MVC中添加並配置 AOP
    • 在Maven中添加AOP依賴
    <!--
        只須要導入 spring-webmvc 這一個包,maven就會自動下載如下依賴包
        spring-core —— Spring的核心組件
        spring-beans —— SpringIoC(依賴注入)的基礎實現
        spring-aop ——Spring的面向切面編程,提供AOP(面向切面編程)實現
        spring-context —— Spring提供在基礎IoC功能上的擴展服務
        spring-expression —— Spring表達式語言
        spring-web —— SpringMVC支持WEB端應用部署架構
        spring-webmvc —— REST Web服務和Web應用的視圖控制器的實現
    -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    
    <!-- aop aspect 相關jar包-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>${aspectj.version}</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>${aspectj.version}</version>
    </dependency>
    複製代碼
    • 在spring-MVC配置文件中,添加配置
    <!-- 激活Spring組件掃描功能,自動掃描指定包及其子包下面經過註解配置的組件 -->
    <context:component-scan base-package="com.test.aop"/>
    
    <!-- 啓動AspectJ支持proxy-target-class="true"指定spring使用cglib來生成代理方法。
    不填Spring則根據條件從cglib和java動態代理中選擇 -->
    <aop:aspectj-autoproxy proxy-target-class="true" />
    複製代碼
  • Spring Boot添加並配置AOP
<!-- 只須要導入這個包,Maven就會下載如下依賴包 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
複製代碼

因爲用的是SpringBoot,因此也不須要配置aopweb

切面表達式

概覽

匹配包/類型

within()

// 匹配service類裏頭的全部方法
@Pointcut("within(com.test.service)")
public void matchType(){}

// 匹配com.test包及子包下全部類的方法
@Pointcut("within(com.test..*)"
public void matchPackage(){}
複製代碼

匹配註解

@annotation()

/**
 * @annotation 匹配方法級別
 * 以下,匹配 標註有 @ToExcel註解 的方法
 */
@Pointcut("@annotation(com.demo.security.ToExcel)")
public void annotation(){}
複製代碼

@within()

/**
 * @within 匹配類級別
 * 非 Spring環境下,要求的 annotation 的 RetentionPolicy 級別爲 CLASS
 * 以下,匹配 標註有 @Service註解 的類 的全部方法
 */
@Pointcut("@within(org.springframework.stereotype.Service)")
public void within(){}
複製代碼

@target()

/**
 * @target 匹配類級別
 * 非 Spring環境下,要求的 annotation 的 RetentionPolicy 級別爲 RUNTIME
 * 以下,匹配 標註有 @Service註解 的類 的全部方法
 */
@Pointcut("@target(org.springframework.stereotype.Service)")
public void target(){}
複製代碼

@args()

/**
 * @args 匹配參數級別
 * 以下,匹配 某方法的參數 所屬的類 標註有 authority註解 的方法
 * 即,被攔截的方法的參數中,有的參數所屬的類 標註有 authority註解
 */
@Pointcut("@args(com.test.authority)")
public void args(){}
複製代碼

匹配對象

this()

// ps:這個還沒弄清楚,就不誤人子弟了
@Pointcut("this(com.test.DemoDao)")
public void thisDemo() {}
複製代碼

target()

// ps:這個還沒弄清楚,就不誤人子弟了
@Pointcut("target(com.test.IDao)")
public void targetDemo() {}
複製代碼

bean()

// 匹配 Spring bean 容器中,全部名稱以 Service 結尾的 bean
@Pointcut("bean(*Service)")
public void beanDemo() {}
複製代碼

匹配參數

execution()

//匹配任何名稱以 find 開頭並且只有一個 Long 參數的方法
@Pointcut("execution(* *..find*(Long))")
public void execution1() {
}

//匹配任何名稱以 find 開頭的並且第一個參數爲 Long 類型的方法
@Pointcut("execution(* *..find*(Long,..))")
public void execution2() {
}
複製代碼

args()

//匹配任何 只有一個Long參數 的方法
@Pointcut("args(Long)")
public void args1() {
}

//匹配第一個參數爲 Long 類型的方法
@Pointcut("args(Long,..)")
public void args2() {
}
複製代碼

execution()表達式

結構

execution(<修飾符>? <返回值類型> <方法>(<參數列表>) <異常>?)spring

  • 帶?的,是可選項目,其餘的爲必選項

實例

/**
 * execution(<修飾符>? <返回值類型> <方法>(<參數列表>) <異常>?)
 * 以下,
 * 匹配 修飾符爲 public,
 * 返回值類型爲任意類型,
 * 方法爲 com.test.service包中 以Service結尾的類的因此方法,
 * 參數列表爲任意參數,
 * 異常爲java.lang.IllegalAccessException
 * 注意,若是指定了異常,那麼只會匹配 throws 了 指定異常的方法!!!
 */
@Pointcut("execution(public * com.test.service.*Service.*(..) throws java.lang.IllegalAccessException)")
public void execution() {
}
複製代碼

切面的註解

@Pointcut()

做用: 定義一個切入點express

@Pointcut()註解的value參數: 一個切面表達編程

實例

/**
 * @Pointcut 註解,用於定義一個織入點
 *
 * 以下,
 * 匹配 修飾符爲 public,
 * 返回值類型爲任意類型,
 * 方法爲 com.test.service包中 以Service結尾的類的因此方法,
 * 參數列表爲任意參數,
 * 異常爲java.lang.IllegalAccessException
 *
 * 的方法爲織入點
 */
@Pointcut("execution(public * com.test.service.*Service.*(..) throws java.lang.IllegalAccessException)")
public void log() {}
複製代碼

@Before()

做用: 被打上 @Before 註解的方法,會在目標方法執行以前執行bash

@Before()註解的value參數: 除了是一個切面表達式以外,還能夠是一個定義好的織入點架構

實例

/**
 * @Before 註解的參數 能夠是一個切面表達式,也能夠是一個織入點
 * 以下,是一個名爲log()的織入點
 * 此@Before註解 將匹配log()織入點匹配到的方法
 */
@Before("log()")
public void before(){
    System.out.println("此語句輸出在目標方法執行以前");
}
複製代碼

@After()

做用: 被打上 @After 註解的方法,會在目標方法執行以後執行,無論目標方法是否成功執行或拋出異常mvc

@After()註解的value參數: 除了是一個切面表達式以外,還能夠是一個定義好的織入點maven

實例

/**
 * @After 註解的參數 能夠是一個切面表達式,也能夠是一個織入點
 * 以下,此@After 將匹配log()織入點 或 切面表達式匹配到的方法
 */
@After("log() || @annotation(com.demo.security.ToExcel)")
public void After(){
    System.out.println("此語句輸出在目標方法執行以後");
}
複製代碼

@Around()

做用: 被打上 @After 註解的方法,會將目標方法「包圍」起來, 在目標方法執行先後作一些操做spring-boot

@Around()註解的value參數: 除了是一個切面表達式以外,還能夠是一個定義好的織入點

實例

/**
 * 打上 @After 註解的方法,會將目標方法「包圍」起來,在目標方法執行先後作一些操做
 * 以下,將匹配log()織入點中方法參數名爲 token 的方法
 * 並將此token參數 和 ProceedingJoinPoint對象 做爲入參
 */
@Around(value = "log() && args(token)")
public Object Around(ProceedingJoinPoint joinPoint, String token) throws Throwable {
    System.out.println("攔截方法的token參數值爲:" + token);

    System.out.println("此語句輸出在目標方法執行以前");
    try {
        // 執行目標方法,並返回目標方法的執行結果
        Object result = joinPoint.proceed(joinPoint.getArgs());
        return result;
    } catch (Throwable throwable) {
        System.out.println("出現異常");
        // 若是目標方法出現異常,不要`生吞`異常,最好原樣拋出
        throw throwable;
    } finally {
        // @After註解 至關於 finally的語句,無論目標方法是否成功執行或拋出異常,都會執行
        System.out.println("此語句輸出在目標方法執行以後");
    }
}
複製代碼

@AfterReturning()

做用: 此註解與@After註解做用同樣,不一樣之出在於它多了一個 returning參數,能夠用來獲取目標方法返回值,並做爲入參帶入方法中

@AfterReturning()註解的value參數: 除了是一個切面表達式以外,還能夠是一個定義好的織入點

實例

/**
 * 使用 returning 獲取目標方法返回值,並取名爲result,再將result做爲參數帶入方法中
 */
@AfterReturning(value = "log() || @annotation(com.demo.security.ToExcel)", returning = "result")
public void AfterReturning(Object result) {
    // 打印目標方法返回結果
    System.out.println(result);
    System.out.println("此語句輸出在目標方法執行以後");
}
複製代碼

@AfterThrowing()

做用: 此註解不一樣於@After註解,它只有在目標方法拋出異常以後纔會執行

@AfterThrowing()註解的value參數: 除了是一個切面表達式以外,還能夠是一個定義好的織入點

實例

/**
 *使用 throwing 獲取目標方法拋出的異常,並取名爲e,再將 e 做爲參數帶入方法中
 */
@AfterThrowing(value="log()", throwing="e")
public void AfterThrowing(Exception e){
    //處理異常
    e.getMessage();
    System.out.println("此方法在 目標方法拋出異常時 才執行");
}
複製代碼

自定義切面類

首先建立一個java類,而後打上 @Aspect 和 @Component 註解,一個切面就定義好了。

實例

package com.example.demo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
* 自定義切面須要在類上面打上兩個註解
*
* @Aspect註解 用於標識這個類是一個自定義切面
* @Component註解 用於將此類交給 Spring 管理
*/
@Aspect
@Component
public class Test {

   /**
    * @Pointcut 註解,用於定義一個織入點
    * <p>
    * 以下,
    * 匹配 修飾符爲 public,
    * 返回值類型爲任意類型,
    * 方法爲 com.test.service包中 以Service結尾的類的因此方法,
    * 參數列表爲任意參數,
    * 異常爲java.lang.IllegalAccessException
    * <p>
    * 的方法爲織入點
    */
   @Pointcut("execution(public * com.test.service.*Service.*(..) throws java.lang.IllegalAccessException)")
   public void log() {
   }


   /**
    * 打上 @Before 註解的方法,會在目標方法執行以前執行
    *
    * @Before 註解的參數 能夠是一個切面表達式,也能夠是一個織入點
    * 以下,是一個名爲log()的織入點
    * 此@Before註解 將匹配log()織入點匹配到的方法
    */
   @Before("log()")
   public void before() {
       System.out.println("此語句輸出在目標方法執行以前");
   }


   /**
    * 被打上 @After 註解的方法,會在目標方法執行以後執行,無論目標方法是否成功執行或拋出異常
    *
    * @After 註解的參數 能夠是一個切面表達式,也能夠是一個織入點
    * 以下,此@After 將匹配log()織入點 或 切面表達式匹配到的方法
    */
   @After("log() || @annotation(com.demo.security.ToExcel)")
   public void After() {
       System.out.println("此語句輸出在目標方法執行以後");
   }


   /**
    * 打上 @After 註解的方法,會將目標方法「包圍」起來,在目標方法執行先後作一些操做
    * 以下,將匹配log()織入點中方法參數名爲 token 的方法
    * 並將此token參數 和 ProceedingJoinPoint對象 做爲入參
    */
   @Around(value = "log() && args(token)")
   public Object Around(ProceedingJoinPoint joinPoint, String token) throws Throwable {
       System.out.println("攔截方法的token參數值爲:" + token);

       System.out.println("@After此語句輸出在目標方法執行以前");
       try {
           // 執行目標方法,並返回目標方法的執行結果
           Object result = joinPoint.proceed(joinPoint.getArgs());
           return result;
       } catch (Throwable throwable) {
           System.out.println("出現異常");
           // 若是目標方法出現異常,不要`生吞`異常,最好原樣拋出
           throw throwable;
       } finally {
           // @After註解 至關於 finally的語句,無論目標方法是否成功執行或拋出異常,都會執行
           System.out.println("此語句輸出在目標方法執行以後");
       }
   }


   /**
    * 使用 returning 獲取目標方法返回值,並取名爲result,再將result做爲參數帶入方法中
    */
   @AfterReturning(value = "log() || @annotation(com.demo.security.ToExcel)", returning = "result")
   public void AfterReturning(Object result) {
       // 打印目標方法返回結果
       System.out.println(result);
       System.out.println("此語句輸出在目標方法執行以後");
   }

   /**
    * 使用 throwing 獲取目標方法拋出的異常,並取名爲e,再將 e 做爲參數帶入方法中
    */
   @AfterThrowing(value = "log()", throwing = "e")
   public void AfterThrowing(Exception e) {
       //處理異常
       e.getMessage();
       System.out.println("此方法在 目標方法拋出異常時 才執行");
   }
}
複製代碼
相關文章
相關標籤/搜索