一、多切面的执行顺序
1、切面中只有普通通知
BValidateAspect切面:
@Aspect
@Component
public class BValidataAspect {
@Before("com.njf.aop.utils.LogUtils.myPoint()")
public static void logStart(JoinPoint joinpoint){
Object[] args = joinpoint.getArgs();
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
}
@AfterReturning(value = "com.njf.aop.utils.LogUtils.myPoint()", returning = "result")
public static void logReturn(JoinPoint joinpoint, Object result){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
}
@AfterThrowing(value = "com.njf.aop.utils.LogUtils.myPoint()", throwing = "exception")
public static void logException(JoinPoint joinpoint, Exception exception){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
}
@After(value = "com.njf.aop.utils.LogUtils.myPoint()")
public static void logEnd(JoinPoint joinpoint){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata后置通知]【"+ methodName +"】方法执行最终完成");
}
}
LogUtils 切面:
@Aspect
@Component
public class LogUtils {
@Pointcut("execution(public int com.njf.aop.calc.MyMathCalculator.*(int, int))")
public void myPoint(){}
@Before("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
public static void logStart(JoinPoint joinpoint){
Object[] args = joinpoint.getArgs();
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
}
//想在目标方法正执行完毕之后
@AfterReturning(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", returning = "result")
public static void logReturn(JoinPoint joinpoint, Object result){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
}
@AfterThrowing(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", throwing = "exception")
//想在目标方法出现异常时执行
public static void logException(JoinPoint joinpoint, Exception exception){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
}
//想在目标方法结束时执行
@After("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
public static void logEnd(JoinPoint joinpoint){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils后置通知]【"+ methodName +"】方法执行最终完成");
}
测试运行:
可以发现是 ValidataAspect 切面先执行前置,然后 LogUtils 前置,然后继续 LogUtils 里面的后置与返回,最后是 ValidataAspect 的后置与返回。
图解:
注意:切面的顺序是按照类名的字母顺序来执行的。
2、使用注解指定切面运行顺序
(1)在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的;
(2)切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定;
(3)实现 Ordered 接口,getOrder() 方法的返回值越小,优先级越高;
(4)若使用 @Order 注解,序号出现在注解中
(5)@Order(1),定义切面作用的优先级,值越小优先级越高,默认值为int的最大值
案例:
@Component
@Aspect
@Order(0) //该切面先执行,序号越小,越先执行
public class TestAspect {
@Before(value = "execution(public int com.spring.aop.ICalc.add(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法之前之前======");
}
}
@Component
@Aspect
@Order(1) //该切面后执行
public class LoggerAspect {
@Before(value = "execution(public int com.spring.aop.ICalc.add(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法执行之前");
Object[] args = joinPoint.getArgs(); //获取方法的参数
String methodName = joinPoint.getSignature().getName(); //获取方法名
System.out.println("方法名:" + methodName + ",参数:" + Arrays.toString(args));
}
}
3、加入环绕通知
在 LogUtils 类中添加环绕通知,并且添加 @Order注解指定运行顺序
LogUtils类:
@Aspect
@Component
@Order(1)
public class LogUtils {
@Pointcut("execution(public int com.njf.aop.calc.MyMathCalculator.*(int, int))")
public void myPoint(){}
@Before("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
public static void logStart(JoinPoint joinpoint){
Object[] args = joinpoint.getArgs();
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
}
//想在目标方法正执行完毕之后
@AfterReturning(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", returning = "result")
public static void logReturn(JoinPoint joinpoint, Object result){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
}
@AfterThrowing(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", throwing = "exception")
//想在目标方法出现异常时执行
public static void logException(JoinPoint joinpoint, Exception exception){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
}
//想在目标方法结束时执行
@After("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
public static void logEnd(JoinPoint joinpoint){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils后置通知]【"+ methodName +"】方法执行最终完成");
}
@Around("myPoint()")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
Object proceed = null;
try {
//@Before
System.out.println("【环绕前置通知】---"+ methodName +"---【方法开始】");
//就是利用反射调用目标方法即可,就是 method.invoke(obj, args)
proceed = pjp.proceed(args);
//@AfterReturning
System.out.println("【环绕返回通知】---"+ methodName +"---【方法返回,返回值 "+ proceed +"】");
} catch (Exception e) {
//@AfterThrowing
System.out.println("【环绕异常通知】---"+ methodName +"---【方法出现异常】,异常信息:" + e);
//为了让外界能知道这个异常,这个异常一定要抛出去
throw new RuntimeException(e);
} finally {
//@After
System.out.println("【环绕后置通知】---"+ methodName +"---【方法结束】");
}
//反射调用后的返回值也一定返回出去,不返回会空指针
return proceed;
}
}
BValidataAspect 类:
@Aspect
@Component
@Order(2)
public class BValidataAspect {
@Before("com.njf.aop.utils.LogUtils.myPoint()")
public static void logStart(JoinPoint joinpoint){
Object[] args = joinpoint.getArgs();
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
}
@AfterReturning(value = "com.njf.aop.utils.LogUtils.myPoint()", returning = "result")
public static void logReturn(JoinPoint joinpoint, Object result){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
}
@AfterThrowing(value = "com.njf.aop.utils.LogUtils.myPoint()", throwing = "exception")
public static void logException(JoinPoint joinpoint, Exception exception){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
}
@After(value = "com.njf.aop.utils.LogUtils.myPoint()")
public static void logEnd(JoinPoint joinpoint){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata后置通知]【"+ methodName +"】方法执行最终完成");
}
}
测试运行:
LogUtils 类中加入了环绕通知,不会影响 BValidataAspect,环绕只是影响当前切面。
图解:
先执行各切面的前置,再执行目标方法,根据从内到外的顺序执行,(环绕只影响添加它的切面,且他在添加它的前面中优先级高)