在项目开的时候遇到一种情况,日志Aspect类是将所有的controller下的所有方法都切到,对某些操作进行留痕却只是其中的一些方法,定义了两个aspect类,但是对两者的执行顺序却不是很清楚。 在网上查找了一些资料,结合官方文档,总结出这篇文章。
第一种情况
@Around(value = "pointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("AROUND BEFORE");
Object proceed = point.proceed();
System.out.println("AROUND AFTER");
return proceed;
}
@Before(value = "pointCut()")
public void beforeNotice(JoinPoint point) {
System.out.println("Before");
}
@After(value = "pointCut()")
public void afterNotice(JoinPoint point) {
System.out.println("After");
}
@AfterReturning(value = "pointCut()", returning = "result")
public void afterReturnNotice(JoinPoint point, Object result) {
System.out.println("AfterReturning");
}
@AfterThrowing(value = "pointCut()", throwing = "ex")
public void afteThrowingNotice(JoinPoint point, Exception ex) {
System.out.println("AfterThrowing");
}
结果:
AROUND BEFORE
Before
AROUND AFTER
After
AfterReturning
上述代码能看出,当所有的通知一起存在时,首先执行环绕通知。
第二种情况
@Around(value = "pointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("AROUND BEFORE");
Object proceed = point.proceed();
System.out.println("AROUND AFTER");
return proceed;
}
@Before(value = "pointCut()")
public void beforeNotice(JoinPoint point) {
System.out.println("Before");
}
@Before(value = "pointCut()")
public void beforeNotice2(JoinPoint point) {
System.out.println("Before2");
}
@After(value = "pointCut()")
public void afterNotice(JoinPoint point) {
System.out.println("After");
}
@After(value = "pointCut()")
public void afterNotice2(JoinPoint point) {
System.out.println("After2");
}
@AfterReturning(value = "pointCut()", returning = "result")
public void afterReturnNotice(JoinPoint point, Object result) {
System.out.println("AfterReturning");
}
@AfterThrowing(value = "pointCut()", throwing = "ex")
public void afteThrowingNotice(JoinPoint point, Exception ex) {
System.out.println("AfterThrowing");
}
结果
AROUND BEFORE
Before
Before2
AROUND AFTER
After
After2
AfterReturning
上述代码能看出,在同一个类中有两个切面同时织入一个切点时,先声明的先执行。
第三种情况 两个Aspect类 使用 org.springframework.core.annotation.Order 注解,值小的先执行。 实现 org.springframework.core.Ordered 重写
@Override
public int getOrder() {
return 0;
}
// 作用和使用注解相同,值越小越先执行,在退出的的时候,值大的先执行
参考spring官方文档中关于Advice Ordering的表述
What happens when multiple pieces of advice all want to run at the same join point? Spring AOP follows the same precedence rules as AspectJ to determine the order of advice execution. The highest precedence advice runs first “on the way in” (so, given two pieces of before advice, the one with highest precedence runs first). “On the way out” from a join point, the highest precedence advice runs last (so, given two pieces of after advice, the one with the highest precedence will run second).
当多个通知希望在同一个切点运行时会发生什么?Spring Aop 与 AspectJ 保持相同的优先级来确定执行的顺序。在执行过程中高优先级的先执行(因此,给定两个前置通知,高优先级首先运行),方法执行完成之后高优先级的通知后执行(因此,给定两个后置通知的情况下,高优先级的将被排在第二位)
When two pieces of advice defined in different aspects both need to run at the same join point, unless you specify otherwise, the order of execution is undefined. You can control the order of execution by specifying precedence. This is done in the normal Spring way by either implementing the org.springframework.core.Ordered interface in the aspect class or annotating it with the Order annotation. Given two aspects, the aspect returning the lower value from Ordered.getValue() (or the annotation value) has the higher precedence.
当不同切面的两个通知需要在相同的切点运行时,除非你特殊指定,否则这个执行是没有顺序的。你可以指定优先级来指定执行的顺序。通过实现spring中提供的org.springframework.core.Ordered 接口或者在切面实体类上使用 @Order 注解。给定两个切面,return 时 Ordered.getValue() (或注解值)比较低的具有高优先级。
When two pieces of advice defined in the same aspect both need to run at the same join point, the ordering is undefined (since there is no way to retrieve the declaration order through reflection for javac-compiled classes). Consider collapsing such advice methods into one advice method per join point in each aspect class or refactor the pieces of advice into separate aspect classes that you can order at the aspect level.
当两个切面定义的通知方法都需要在同一切点上运行时,其顺序是未定义的(因为无法通过反射为javac编译的类检索声明顺序)。 考虑将这些建议方法折叠为每个方面类中每个连接点的一个通知方法,或将建议重构为单独的切面类,您可以在方面级别进行定制。