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("此方法在 目标方法抛出异常时 才执行");
   }
}
复制代码