Spring(十九):Spring AOP:切面的优先级
Posted yy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring(十九):Spring AOP:切面的优先级相关的知识,希望对你有一定的参考价值。
背景:
1)指定切面优先级示例:有的时候需要对一个方法指定多个切面,而这多个切面有时又需要按照不同顺序执行,因此,切面执行优先级别指定功能就变得很实用。
2)重复使用切入点表达式:上一篇文章中,定义前置、后置、返回、异常通知的切入点表达式时,都使用了同一个;而且本章节新加入的验证切面ValidateAspect类,也使用同一个切入点表达式,怎么让他们重用呢?
指定切面优先级示例:
比如在算术计算器执行计算之前进行数据验证,验证完事之后才让执行日志输出。
新建spring aop项目参考:《Spring(十八):Spring AOP(二):通知(前置、后置、返回、异常、环绕)》
添加验证切面类:
package com.dx.spring.beans.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class ValidateAspect { @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(..))") public void beforeMethod() { System.out.println("validate..."); } }
除了验证切面,还包含日志切面:
package com.dx.spring.beans.aop; import java.util.List; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; //把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。 @Aspect @Component public class LoggingAspect { // 声明该方法为一个前置通知:在目标方法开始之前执行 @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))") public void beforeMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); List<Object> args = Arrays.asList(joinpoint.getArgs()); System.out.println("before method " + methodName + " with " + args); } // 声明该方法为一个后置通知:在目标方法结束之后执行(无论方法是否抛出异常)。 // 但是因为当方法抛出异常时,不能返回值为null,因此这里无法获取到异常值。 @After(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int,int))") public void afterMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); List<Object> args = Arrays.asList(joinpoint.getArgs()); System.out.println("after method " + methodName); } /** * 声明该方法为一个返回通知:在目标方法返回结果时后执行(当方法抛出异常时,无法执行)。 但是因为当方法抛出异常时,类似代码 {@code * try <br/> * { <br/> * // before 前置通知 <br/> * // do action method <br/> * // after returning 返回通知,可以访问到方法的返回值 <br/> * } catch(Exception e){ e.printStackTrace(); // after throwing * 异常通知,可以访问到方法出现的异常 } // after 后置通知,因为方法可以抛出异常,所以访问不到方法的返回值 } */ @AfterReturning(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int,int))", returning = "result") public void afterReturningMethod(JoinPoint joinpoint, Object result) { String methodName = joinpoint.getSignature().getName(); List<Object> args = Arrays.asList(joinpoint.getArgs()); System.out.println( "after method " + methodName + " with returning " + (result == null ? "NULL" : result.toString())); } /** * 定义一个异常通知函数: 只有在方法跑吹异常时,该方法才会执行,而且可以接受异常对象。 */ @AfterThrowing(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int,int))", throwing = "ex") public void afterThrowingMethod(JoinPoint joinpoint, Exception ex) { String methodName = joinpoint.getSignature().getName(); List<Object> args = Arrays.asList(joinpoint.getArgs()); System.out.println( "after method " + methodName + " occurs exception: " + (ex == null ? "NULL" : ex.getMessage())); } // @Around(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))") // public Object aroundMethod(ProceedingJoinPoint pJoinPoint) throws Exception { // String methodName = pJoinPoint.getSignature().getName(); // List<Object> args = Arrays.asList(pJoinPoint.getArgs()); // // Object result = null; // try { // // 前置通知 // System.out.println("before method " + methodName + " with " + args); // // 执行目标方法 // result = pJoinPoint.proceed(); // // 返回通知 // System.out.println("after method " + methodName + " returning " + result); // } catch (Throwable ex) { // // 异常通知 // System.out.println("after method " + methodName + " occurs exception: " + ex.getMessage()); // throw new Exception(ex); // } // // 后置通知 // System.out.println("after method " + methodName); // return result; // } }
默认打印结果:
按照实际业务需求来讲,更希望是先执行验证切面,才执行日志切面,那么,可以通过设置切面优先级别来设置:
修改ValidateAspect.java添加注解@Order(1)
@Order(value=1) @Component @Aspect public class ValidateAspect { @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(..))") public void beforeMethod() { System.out.println("validate..."); } }
修改LoggingAspect.java添加注解@Order(2)
@Order(value=2) @Aspect @Component public class LoggingAspect { //代码 }
此时执行结果为:
重使用切入点表达式:
上一篇文章中,定义前置、后置、返回、异常通知的切入点表达式时,都使用了同一个;而且本章节新加入的验证切面ValidateAspect类,也使用同一个切入点表达式,怎么让他们重用呢?
第一步:修改LoggingAspect切面类,添加@Pointcut注解的方法declareAspectJoinPointExpression():
//把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。 @Order(value=2) @Aspect @Component public class LoggingAspect { @Pointcut("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(..))") public void declareAspectJoinPointExpression(){ } //... }
第二步:修改LoggingAspect切面类,把前置、后置、返回、异常、环绕通知方法的切入点表达式替换为“declareAspectJoinPointExpression()”
以前置通知方法为例:
//把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。 @Order(value=2) @Aspect @Component public class LoggingAspect { @Pointcut("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(..))") public void declareAspectJoinPointExpression(){ } // 声明该方法为一个前置通知:在目标方法开始之前执行 @Before("declareAspectJoinPointExpression()") public void beforeMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); List<Object> args = Arrays.asList(joinpoint.getArgs()); System.out.println("before method " + methodName + " with " + args); } //... }
第三步:修改验证切面类ValidateAspect.java:
@Order(value=1) @Component @Aspect public class ValidateAspect { @Before("com.dx.spring.beans.aop.LoggingAspect.declareAspectJoinPointExpression()") public void beforeMethod() { System.out.println("validate..."); } }
备注:如果在同一个包下,可以不指定包的路径。
第四步:验证
以上是关于Spring(十九):Spring AOP:切面的优先级的主要内容,如果未能解决你的问题,请参考以下文章
Spring框架系列 - Spring AOP实现原理详解之AOP切面的实现