SpringBoot整合AOP

引入Maven依赖

<!-- 引入aop支持 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

使用详解

切面类

  1. 在类上使用@Component注解
  2. 在类上使用@Aspect注解 使之成为切面类
/**
 * @author xhy
 * @date 2021/7/2 9:53
 */
@Aspect
@Component
public class ExceptionAOP {

    @Pointcut(value = "") //其中value可以写多种表达式定义切入点,后续详解
    public void pointCut() {
    }

    /**
     *前置通知,在切点执行之前执行的操作
     *
     */
    @Before("pointCut()")
    public void before(JoinPoint joinPoint) {
      	// 逻辑代码
    }
    
    /**
    * 其他通知类型,具体支持的类型见下文
    */
}
@PointCut注解详解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Pointcut {
    String value() default "";

    String argNames() default "";
}

切入点表达式包括一个execution表达式和argNames

例如:

@Pointcut("execution(* info.csdc.smdb.project.service.*.*(..))")
public void pointCut() {}

PointCut是指哪些方法需要被执行"AOP",PointCut表达式可以有一下几种方式:

  • execution
    execution([可见性]返回类型[声明类型].方法名(参数)[异常]),其中[]内的是可选的,其他的还支持通配符的使用:
  1. *: 匹配所有
  2. …: 匹配多个包或多个参数
  3. +: 表示类及其子类
  4. 运算符:&&、||、!
  • within
    是用来指定类型的,指定类型中的所有方法将被拦截是用来指定类型的,指定类型中的所有方法将被拦截
  • within(com.demo.service.impl.UserServiceImpl) 匹配UserServiceImpl类对应对象的所有方法调用,并且只能是UserServiceImpl对象,不能是它的子对象
  • within(com.demo…*)匹配com.demo包及其子包下面的所有类的所有方法的外部调用
  • this
    SpringAOP是基于代理的,this就代表代理对象,语法是this(type),当生成的代理对象可以转化为type指定的类型时表示匹配。
  • this(com.demo.service.IUserService)匹配生成的代理对象是IUserService类型的所有方法的外部调用
  • target
    SpringAOP是基于代理的,target表示被代理的目标对象,当被代理的目标对象可以转换为指定的类型时则表示匹配。
  • target(com.demo.service.IUserService) 匹配所有被代理的目标对象能够转化成IuserService类型的所有方法的外部调用。
  • args
    args用来匹配方法参数
  • args() 匹配不带参数的方法
  • args(java.lang.String) 匹配方法参数是String类型的
  • args(…) 带任意参数的方法
  • args(java.lang.String,…) 匹配第一个参数是String类型的,其他参数任意。最后一个参数是String的同理。
  • @within 和 @target
    带有相应标注的所有类的任意方法,比如@Transactional
@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)
@annotation

带有相应标注的任意方法,比如@Transactional或其他自定义注解

@annotation(org.springframework.transaction.annotation.Transactional)
@args

参数带有相应标注的任意方法

AOP支持的通知类型

  1. 前置通知@Before:在某连接点之前执行的通知
@Before(value = "pointCut()")
public void before(JoinPoint joinPoint){
    ...
}
  1. 后置返回通知@AfterReturning:在某连接点之后执行的通知,通常在一个匹配的方法返回的时候执行(可以在后置通知中绑定返回值)
/** 
 * 后置返回通知 
 * 这里需要注意的是: 
 *      如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息 
 *      如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数 
 *      returning:限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,
 *      对于returning对应的通知方法参数为Object类型将匹配任何目标返回值 
 * @param joinPoint 
 * @param keys 
 */  
@AfterReturning(value = "pointCut()",returning = "keys")  
public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){  
    logger.info("第一个后置返回通知的返回值:"+keys);  
}  

@AfterReturning(value = "pointCut()",returning = "keys",argNames = "keys")  
public void doAfterReturningAdvice2(String keys){  
    logger.info("第二个后置返回通知的返回值:"+keys);  
}
  1. 后置异常通知@AfterThrowing
/** 
 * 后置异常通知 
 *  定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法; 
 *  throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行, 
 *           对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。 
 * @param joinPoint 
 * @param exception 
 */  
@AfterThrowing(value = "pointCut()",throwing = "exception")  
public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){  
    //目标方法名
    logger.info(joinPoint.getSignature().getName());  
    if(exception instanceof NullPointerException){  
        logger.info("发生了空指针异常!!!!!");  
    }  
}
  1. 后置最终通知@After
/** 
 * 后置最终通知(目标方法只要执行完了就会执行后置通知方法) 
 * @param joinPoint 
 */  
@After(value = "pointCut()")  
public void doAfterAdvice(JoinPoint joinPoint){ 
    logger.info("后置最终通知执行了!!!!");  
}
  1. 环绕通知@Around
/** 
 * 环绕通知: 
 *   环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 
 *   环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型 
 */  
@Around(value = "pointCut()")  
public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){  
    logger.info("环绕通知的目标方法名:"+proceedingJoinPoint.getSignature().getName());  
    try {  
        Object obj = proceedingJoinPoint.proceed();  
        return obj;  
    } catch (Throwable throwable) {  
        throwable.printStackTrace();  
    }  
    return null;  
}

JoinPoint和ProceedingJoinPoint使用详解

JoinPoint可获取的信息
//返回目标对象,即被代理的对象
Object getTarget();

//返回切入点的参数
Object[] getArgs();

//返回切入点的Signature
Signature getSignature();

//返回切入的类型,比如method-call,field-get等等
String getKind();
ProceedingJoinPoint

Proceedingjoinpoint 继承了 JoinPoint。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。

JoinPoint使用
  • 获取切入点所在目标对象
Object targetObj =joinPoint.getTarget();

// 可以发挥反射的功能获取关于类的任何信息,例如获取类名如下
String className = joinPoint.getTarget().getClass().getName();

  • 获取切入点方法的名字
// getSignature())是获取到这样的信息 :修饰符+ 包名+组件名(类名) +方法名
Signature signature = joinPoint.getSignature();
// 通过Signature可以使用反射获取所有对象以及方法的信息
  • 获取参数列表
Object[] args = joinPoint.getArgs();