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注解指定该切面最后一个执行