之前我们提过的应用场景,一个原始对象可能会需要插入多个切面,如果我们按前几篇博客文章介绍的方法完成切面及其通知的注解声明,那么它的执行顺序是怎么样的呢?
本文将介绍AspectJ的切面如何划分优先级。
指定切面的优先级
在同一个连接点上应用不止一个切面时, 除非明确指定, 否则它们的优先级是不确定的.
切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定.
实现 Ordered 接口, getOrder() 方法的返回值越小, 优先级越高.
若使用 @Order 注解, 序号出现在注解中
好我们还是在前一篇的例子上做修改:
我们上一篇已经有了一个输出日志的切面:
package com.happBKs.spring.aopbasic.aop1; import java.util.Arrays; import java.util.List; 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.core.annotation.Order; import org.springframework.stereotype.Component; @Order(2) @Aspect @Component public class LoggingAspect { /* * 在com.happBKs.spring.aopbasic.aop1.AtithmeticCalculate接口的每一个实现类的每一个方法开始之前执行 */ @Before("execution(public int com.happBKs.spring.aopbasic.aop1.AtithmeticCalculate.*(..))") public void beforeMethod(JoinPoint joinPoint) { String methodName=joinPoint.getSignature().getName(); List<Object> args=Arrays.asList(joinPoint.getArgs()); System.out.println("begin to "+methodName+" with "+args); } /* * 在目标方法执行之后(无论是否发生异常),执行该通知 */ @After("execution(public int com.happBKs.spring.aopbasic.aop1.AtithmeticCalculate.*(..))") public void afterMethod(JoinPoint joinPoint) { String methodName=joinPoint.getSignature().getName(); System.out.println("end to "+methodName); } /* * 在目标方法正常执行之后执行的代码 * 返回通知是可以访问目标方法的返回值的 */ @AfterReturning(value="execution(public int com.happBKs.spring.aopbasic.aop1.AtithmeticCalculate.*(..))", returning="resultParam") public void afterReturning(JoinPoint joinPoint, Object resultParam) { String methodName=joinPoint.getSignature().getName(); System.out.println("after "+methodName+" with returning result "+resultParam); } /* * 在目标方法出现异常时出现代码 * 可以访问到异常对象,且可以指定在特定异常时才执行代码 */ @AfterThrowing(value="execution(public int com.happBKs.spring.aopbasic.aop1.AtithmeticCalculate.*(..))", throwing="exThrowing") public void afterThrowing(JoinPoint joinPoint, Exception exThrowing) { String methodName=joinPoint.getSignature().getName(); System.out.println("after exception of "+methodName+" we find the exception is "+exThrowing); } /* * 回环通知需要携带ProceedingJoinPoint类型参数 * 回环通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法 * 且回环通知必须有返回值,返回值即为目标方法的返回值 */ @Around(value="execution(public int com.happBKs.spring.aopbasic.aop1.AtithmeticCalculate.*(..))") public Object roundingMethod(ProceedingJoinPoint pjp) { Object result=null; String methodName=pjp.getSignature().getName(); try { //前置通知 System.out.println("Around: Begin Method"+methodName+" executed with "+Arrays.asList(pjp.getArgs())); result= pjp.proceed(); //返回通知 System.out.println("Around: Return Method"+methodName+" with result"+result); } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); //异常通知 System.out.println("Around: Exception in "+methodName+":"+e ); } //后置通知 System.out.println("Around: end Method"+methodName); return 100; } }
我们再定义一个数据验证切面:
package com.happBKs.spring.aopbasic.aop1; import java.util.Arrays; import java.util.List; 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.core.annotation.Order; import org.springframework.stereotype.Component; @Order(Integer.MAX_VALUE) @Aspect @Component public class DataValidateAspect { @Before("execution(public int com.happBKs.spring.aopbasic.aop1.AtithmeticCalculate.*(..))") public boolean beforeMethod(JoinPoint joinPoint) { String methodName=joinPoint.getSignature().getName(); List<Object> args=Arrays.asList(joinPoint.getArgs()); System.out.println("data validate---begin to "+methodName+" with "+args); if((Integer)(args.get(0))>0&&(Integer)(args.get(1))>0) { System.out.println("data is OK"); return true; } else { System.out.println("data is bad"); return false; } } }
我们这里已经为它们两个指定好了Order的值,值越小,优先级越高。不过,这里为了说明问题,我们再加入一个没有设置Order注解的切面:
package com.happBKs.spring.aopbasic.aop1; import java.util.Arrays; import java.util.List; 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.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component public class DataValidateAspect2 { @Before("execution(public int com.happBKs.spring.aopbasic.aop1.AtithmeticCalculate.*(..))") public boolean beforeMethod(JoinPoint joinPoint) { String methodName=joinPoint.getSignature().getName(); List<Object> args=Arrays.asList(joinPoint.getArgs()); System.out.println("data validate 2---begin to "+methodName+" with "+args); if((Integer)(args.get(0))<10000&&(Integer)(args.get(1))<10000) { System.out.println("data is OK"); return true; } else { System.out.println("data is bad"); return false; } } }
Spring配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> <context:component-scan base-package="com.happBKs.spring.aopbasic.aop1"></context:component-scan> <!-- 使用AspectJ注解起作用。自动为匹配的类生成代理对象 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
测试程序:
package com.happBKs.spring.aopbasic; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.happBKs.spring.aopbasic.aop1.AtithmeticCalculate; public class TestSpringAOP { @Test public void testSpringAOP() { //1. 创建spring 的 IOC 容器 ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml"); //2. 从IOC容器获取bean实例 AtithmeticCalculate atithmeticCalculate = (AtithmeticCalculate)ac.getBean(AtithmeticCalculate.class); //考察一下代理对象是否生成 System.out.println(atithmeticCalculate.getClass().getName()); //3. 使用bean System.out.println("Example 1:"); int result=atithmeticCalculate.add(10, 5); System.out.println(result); } }
运行结果:
com.sun.proxy.$Proxy13
Example 1:
Around: Begin Methodadd executed with [10, 5]
begin to add with [10, 5]
data validate---begin to add with [10, 5]
data is OK
data validate 2---begin to add with [10, 5]
data is OK
Around: Return Methodadd with result15
Around: end Methodadd
end to add
结论:
可以看到切面的优先级为,有@Order注解的比没有@Order注解的先执行,@Order值小的比大的先执行。