springboot学习5:使用aop

一、添加依赖

<!--aop-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

二、编写切面和切入点以及要执行的通知

引用依赖后就可以直接使用,不需要做其他额外的配置

在切面类上加上@Aspect注解类声明当前类是一个切面类,再加上@Component注解注册为bean对象

三、多个aop指向了同一个方法时的执行顺序

默认情况下是随机的,可以通过在切面类上加@Order注解来指定顺序,该注解支持传入一个int型的value,数值越小的切面先执行。

注意,先执行的aop一定后结束,可以把aop和原来方法的执行过程理解成一个同心圆,原来方法就是中间的圆心,

如下图所示

程序从左向右开始执行,先执行aop1的前置通知,再执行aop2的前置通知,然后再执行原来的方法逻辑,然后依次是aop2的后置通知,aop1的后置通知。如果使用的是环绕通知,要先把1和2两个通知中

。。。前
point.proceed(point.getArgs());//执行原来方法
。。。后

之前的逻辑按先1后2的顺序执行完,才会去执行这一句。方法执行完成之后按照先2后1的顺序执行后边的逻辑。

四、使用aop实现MDC日志埋点和异常处理

通过aop拦截到controller方法,给设置mdc标识

通过aop拦截到controller方法,对controller方法可能抛出的异常进行处理

分析:mdc埋点的aop切面和异常处理的aop切面都会切入到同一controller方法上,所以需要对aop的执行顺序进行配置。按我们的需求,需要在异常处理的切面中触发对原方法的执行,否则如果在mdc埋点的切面中触发了对原方法的执行,该切面中就会收到controller层的异常,我们的目的是让异常处理切面收到异常进行处理。

所以aop的执行顺序应该是先执行mdc埋点的aop,再执行异常处理的aop。

当然最好的方案是把这两个aop切面合成一个,这里是为了演示aop的执行顺序才拆成了两个

mdc埋点的切面



@Aspect
@Component
@Order(1)
public class MdcValSetAspect {

    private final static Logger LOGGER= LoggerFactory.getLogger(CheckInLogAspectConfig.class);

    @Pointcut("execution( public * com.lyy.controller.*.*(..))")
    public void mdcValuePointcut(){}

    @Around("mdcValuePointcut()")
    public Object mdcValueSet(ProceedingJoinPoint point){
        String mdcValue= UUID.randomUUID().toString();
        System.out.println("mdcValueSet is run");
        MDC.put("MDC_VALUE", mdcValue);
        try {
            return point.proceed(point.getArgs());
        } catch (Throwable throwable) {
            LOGGER.error("mdcValueSet error",throwable);
            return ResultVoUtil.error(new MyException(9998,"mdcValueSet error:"+throwable.toString()));
        }
    }
}

使用order注解指定该切面第一个执行

异常处理的切面

@Aspect
@Component
@Order(Integer.MAX_VALUE)
public class ExceptionHandlerAspect {

    private final static Logger LOGGER= LoggerFactory.getLogger(ExceptionHandlerAspect.class);

    @Pointcut("execution(public * com.lyy.controller.*.*(..))")
    public void exceptionHandlerPointcut(){}

    @Around("exceptionHandlerPointcut()")
    public Object exceptionHandler(ProceedingJoinPoint point){
        String methodFullName=point.getSignature().getDeclaringTypeName()+"."+point.getSignature().getName();
        try {
            return point.proceed(point.getArgs());
        } catch (Throwable throwable) {
            LOGGER.error(methodFullName+" is error,e:",throwable);
            return ResultVoUtil.error(new MyException(9999,"未知异常,"+throwable.toString()));
        }
    }
}

使用Order注解指定该切面最后一个执行