SpringAOP@Before@Around@After等advice的排列顺序版权声明

原创
小哥 3年前 (2022-11-11) 阅读数 10 #大杂烩

用过spring开发框架的人或多或少都会使用它。AOP功能,大家都知道@Before、@Around和@After等advice。最近,为了在项目中实现输出日志和权限控制这两个要求,我也使用了它们。AOP作用我用过了@Before、@Around这两个advice然而,在使用过程中,它们的执行顺序并不清楚。为了理解在不同情况下advice它是按照什么顺序执行的,我做了一个测试,并在这里记录下来,供以后查看。

前提
对于AOP相关类(aspect、pointcut等)本文的概念没有解释。
关于如何制作spring帧扫描AOP,本文也不作解释。
情况一: 方法只是一种。Aspect类拦截
当方法只是一种。Aspect拦截时Aspect中的不同advice它的执行顺序是什么?请看:

添加 PointCut类
该pointcut用来拦截test包下所有类中的所有方法。

package test;

import org.aspectj.lang.annotation.Pointcut;

public class PointCuts {
@Pointcut(value = "within(test.*)")
public void aopDemo() {

}
}

添加Aspect类
该类中的advice将使用以上内容pointcut,请查看每种使用方法advice的value属性。

package test;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class Aspect1 {

@Before(value = "test.PointCuts.aopDemo()")
public void before(JoinPoint joinPoint) {
System.out.println("[Aspect1] before advise");
}

@Around(value = "test.PointCuts.aopDemo()")
public void around(ProceedingJoinPoint pjp) throws  Throwable{
System.out.println("[Aspect1] around advise 1");
pjp.proceed();
System.out.println("[Aspect1] around advise2");
}

@AfterReturning(value = "test.PointCuts.aopDemo()")
public void afterReturning(JoinPoint joinPoint) {
System.out.println("[Aspect1] afterReturning advise");
}

@AfterThrowing(value = "test.PointCuts.aopDemo()")
public void afterThrowing(JoinPoint joinPoint) {
System.out.println("[Aspect1] afterThrowing advise");
}

@After(value = "test.PointCuts.aopDemo()")
public void after(JoinPoint joinPoint) {
System.out.println("[Aspect1] after advise");
}
}

添加测试Controller
添加用于测试的。controller,这个controller中只有一个方法,但它将根据参数值执行不同的操作:一个是正常返回对象,一个是抛出异常。(因为我们必须测试@AfterThrowing这个advice)

package test;

import test.exception.TestException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/aop")
public class AopTestController {

@ResponseStatus(HttpStatus.OK)
@RequestMapping(value = "/test", method = RequestMethod.GET)
public Result test(@RequestParam boolean throwException) {
// case 1
if (throwException) {
System.out.println("throw an exception");
throw new TestException("mock a server exception");
}

// case 2
System.out.println("test OK");
return new Result() { {
this.setId(111);
this.setName("mock a Result");
}};
}

public static class Result {
private int id;
private String name;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
}

测试 正常情况
直接在浏览器中输入以下内容。URL,回车:

http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=false
1
我们将看到输出的结果:

[Aspect1] around advise 1
[Aspect1] before advise
test OK
[Aspect1] around advise2
[Aspect1] after advise
[Aspect1] afterReturning advise

测试 异常情况
直接在浏览器中输入以下内容。URL,回车:

http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=true
1
我们将看到输出的结果:

[Aspect1] around advise 1
[Aspect1] before advise
throw an exception
[Aspect1] after advise
[Aspect1] afterThrowing advise

结论
在方法只是一种。aspect类截距,aspect类内部的 advice 将按以下顺序执行:

正常情况:

异常情况:

情况二: 相同的方法是多重的。Aspect类拦截
这里有一个二的例子。aspect类拦截。
在某些情况下,对于两种不同的aspect类别,无论其advice使用相同的pointcut,或不同pointcut,都有可能导致相同的方法是多重的。aspect类拦截。在这种情况下,这个倍数Aspect类中的advice又它的执行顺序是什么?请看:

pointcut类别保持不变
添加新的aspect类
package test;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class Aspect2 {

@Before(value = "test.PointCuts.aopDemo()")
public void before(JoinPoint joinPoint) {
System.out.println("[Aspect2] before advise");
}

@Around(value = "test.PointCuts.aopDemo()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("[Aspect2] around advise 1");
pjp.proceed();
System.out.println("[Aspect2] around advise2");
}

@AfterReturning(value = "test.PointCuts.aopDemo()")
public void afterReturning(JoinPoint joinPoint) {
System.out.println("[Aspect2] afterReturning advise");
}

@AfterThrowing(value = "test.PointCuts.aopDemo()")
public void afterThrowing(JoinPoint joinPoint) {
System.out.println("[Aspect2] afterThrowing advise");
}

@After(value = "test.PointCuts.aopDemo()")
public void after(JoinPoint joinPoint) {
System.out.println("[Aspect2] after advise");
}
}

测试用Controller也不变
或者使用上面的一个Controller。但现在 aspect1 和 aspect2 将拦截controller中的方法。

让我们继续测试!

测试 正常情况
直接在浏览器中输入以下内容。URL,回车:

http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=false
1
我们将看到输出的结果:

[Aspect2] around advise 1
[Aspect2] before advise
[Aspect1] around advise 1
[Aspect1] before advise
test OK
[Aspect1] around advise2
[Aspect1] after advise
[Aspect1] afterReturning advise
[Aspect2] around advise2
[Aspect2] after advise
[Aspect2] afterReturning advise

但此时,我无法下定决心说 aspect2 肯定就比 aspect1 先执行。
不相信吗?如果重新启动服务服务器并重试,您可能会看到以下执行结果:

[Aspect1] around advise 1
[Aspect1] before advise
[Aspect2] around advise 1
[Aspect2] before advise
test OK
[Aspect2] around advise2
[Aspect2] after advise
[Aspect2] afterReturning advise
[Aspect1] around advise2
[Aspect1] after advise
[Aspect1] afterReturning advise

即在这种情况下, aspect1 和 aspect2 执行顺序未知。如何解决?不要着急,下面将给出解决方案。

测试 异常情况
直接在浏览器中输入以下内容。URL,回车:

http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=true
1
我们将看到输出的结果:

[Aspect2] around advise 1
[Aspect2] before advise
[Aspect1] around advise 1
[Aspect1] before advise
throw an exception
[Aspect1] after advise
[Aspect1] afterThrowing advise
[Aspect2] after advise
[Aspect2] afterThrowing advise

类似地,如果重新启动服务器然后进行测试,您可能会看到以下结果:

[Aspect1] around advise 1
[Aspect1] before advise
[Aspect2] around advise 1
[Aspect2] before advise
throw an exception
[Aspect2] after advise
[Aspect2] afterThrowing advise
[Aspect1] after advise
[Aspect1] afterThrowing advise

即在异常情况下, aspect1 和 aspect2 执行顺序也不确定。

那么在 情况二 在下,如何指定每个 aspect 执行的顺序是什么?
有两种方法:

实现org.springframework.core.Ordered接口,实现其getOrder()方法
给aspect添加@Order注释,统称为:org.springframework.core.annotation.Order
无论使用哪种方法,值越小 aspect 第一次执行的次数越多。
例如,我们 apsect1 和 aspect2 分别添加 @Order 注释如下:

@Order(5)
@Component
@Aspect
public class Aspect1 {
// ...
}

@Order(6)
@Component
@Aspect
public class Aspect2 {
// ...
}

在这种修改之后, aspect1 中的 advice 总是比 aspect2 中的 advice 首先执行。如下图所示:

注意点
如果相同 aspect 类,相同 pointcut,定义了两个相同的 advice(例如,两个 @Before),然后这两个 advice 执行的顺序是不确定的,即使你给出这两个 advice 添加了 @Order 这个注释,也不能。记住这一点。

对于@Around这个advice,无论它是否有返回值,但必须在方法内部调用它。 pjp.proceed();否则,Controller 中的接口将不会被执行,这也导致 @Before这个advice不会触发。例如,让我们假设在正常情况下,执行的顺序是“aspect2 -> apsect1 -> controller“如果,我们把 aspect1中的@Around中的 pjp.proceed();要删除,我们看到的输出将是:

[Aspect2] around advise 1
[Aspect2] before advise
[Aspect1] around advise 1
[Aspect1] around advise2
[Aspect1] after advise
[Aspect1] afterReturning advise
[Aspect2] around advise2
[Aspect2] after advise
[Aspect2] afterReturning advise

从结果可以发现, Controller 中的 接口 未执行,aspect1 中的 @Before advice 它也没有得到实施。

参考资料
Spring 4.3.2.RELEASE 官方信息:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/
其中,AOP执行顺序部分为:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#aop-ataspectj-advice-ordering


作者:rainbow702
来源:CSDN
原文:https://blog.csdn.net/rainbow702/article/details/52185827
版权声明:本文为博主原创文章。转载请附上博客链接!

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除