<返回更多

SpringAOP的切面执行顺序在Spring4和Spring5中有什么区别?

2022-03-03    奋斗的三岁半
加入收藏

准备测试代码

public interface UserService {
    void doProcess(long userId);
}
@Service
public class DefaultUserService implements UserService {

    @Override
    public void doProcess(long userId) {
        if (userId <= 0) {
            throw new IllegalArgumentException("userId <= 0");
        }
    }

}
@Aspect
@Component
public class UserServiceLogAspect {

    @Pointcut("execution(public * com.example.springaop.service.impl.DefaultUserService.*(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("++++++++++> @Before " + joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs()));
    }

    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
        System.out.println("<++++++++++ @After " + joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs()));
    }

    @AfterReturning("pointcut()")
    public void afterReturning(JoinPoint joinPoint) {
        System.out.println("<++++++++++ @AfterReturning " + joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs()));
    }

    @AfterThrowing(value = "pointcut()", throwing = "throwable")
    public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
        System.err.println("<++++++++++ @AfterThrowing "
                           + joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs())
                           + " errMsg=[" + throwable.getMessage() + "]");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        final String sig = joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs());
        Object result = null;
        System.out.println("----------> @Around##Before " + sig);
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            System.err.println("<---------- @Around##AfterThrowing " + sig + " errMsg=[" + e.getMessage() + "]");
            throw e; // re-throw
        }
        System.out.println("<---------- @Around##After " + sig);
        return result;
    }
}

基于以上的切面逻辑,在 UserService#doProcess(long) 方法执行前后以及异常情况下的各个切面的执行顺序是怎样的呢?

在Spring5中的结果

执行下面测试代码,观察输出:

@SpringBootTest
class SpringAopApplicationTestsSpring5 {

    @Autowired
    private UserService userService;

  // 测试正常情况
    @Test
    public void testNormally() {
        System.out.println("SpringBootVersion: " + SpringBootVersion.getVersion() + "tSpringVersion: " + SpringVersion.getVersion() + "n");
        this.userService.doProcess(1);
    }

  // 测试异常情况
    @Test
    public void testError() {
        System.out.println("SpringBootVersion: " + SpringBootVersion.getVersion() + "tSpringVersion: " + SpringVersion.getVersion() + "n");
        this.userService.doProcess(-1);
    }

}
SpringBootVersion: 2.5.7  SpringVersion: 5.3.13

----------> @Around##Before doProcess[1]
++++++++++> @Before doProcess[1]
<++++++++++ @AfterReturning doProcess[1]
<++++++++++ @After doProcess[1]
<---------- @Around##After doProcess[1]
SpringBootVersion: 2.5.7  SpringVersion: 5.3.13

----------> @Around##Before doProcess[-1]
++++++++++> @Before doProcess[-1]
<++++++++++ @AfterThrowing doProcess[-1] errMsg=[userId <= 0]
<++++++++++ @Around##AfterThrowing doProcess[-1] errMsg=[userId <= 0]
<++++++++++ @After doProcess[-1]
<---------- @Around##After doProcess[-1]

在Spring4中的结果

执行下面测试代码,观察输出:

@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringAopApplicationTestsSpring4 {

    @Autowired
    private UserService userService;

    @Test
    public void testNormally() {
        System.out.println("SpringBootVersion: " + SpringBootVersion.getVersion() + "tSpringVersion: " + SpringVersion.getVersion() + "n");
        this.userService.doProcess(1);
    }

    @Test
    public void testError() {
        System.out.println("SpringBootVersion: " + SpringBootVersion.getVersion() + "tSpringVersion: " + SpringVersion.getVersion() + "n");
        this.userService.doProcess(-1);
    }

}
SpringBootVersion: 1.5.9.RELEASE  SpringVersion: 4.3.13.RELEASE

----------> @Around##Before doProcess[1]
++++++++++> @Before doProcess[1]
<---------- @Around##After doProcess[1]
<++++++++++ @After doProcess[1]
<++++++++++ @AfterReturning doProcess[1]
SpringBootVersion: 1.5.9.RELEASE  SpringVersion: 4.3.13.RELEASE

----------> @Around##Before doProcess[-1]
++++++++++> @Before doProcess[-1]
<++++++++++ @After doProcess[-1]
<---------- @Around##AfterThrowing doProcess[-1] errMsg=[userId <= 0]
<++++++++++ @AfterThrowing doProcess[-1] errMsg=[userId <= 0]

结论

在 Spring5 中各个切面的执行顺序如下:

环绕通知 @Around 的逻辑包裹着 @Before、 @AfterReturing、 @AfterThrowing、 @Returing 这些切面。@After 类比于 finally 块的代码一样在最后执行。

在 Spring4 中各个切面的执行顺序如下:

环绕通知 @Around 的逻辑 并不是 包裹着 @Before、 @AfterReturing、 @AfterThrowing、 @Returing 这些切面。@After 在 @AfterReturing 或 @AfterThrowing 之前执行。

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>