当前位置:  首页>> 技术小册>> Spring AOP 编程思想(上)

@AspectJ前置动作:@Before与@Around谁优先级执行?

在Spring AOP(面向切面编程)的广阔领域中,@Before@Around是两个极其重要且常用的注解,它们分别用于定义前置通知(Before Advice)和环绕通知(Around Advice)。理解这两个注解的执行顺序及其背后的机制,对于编写高效、可维护的切面至关重要。本章将深入探讨@Before@Around通知的优先级问题,解析它们在不同场景下的行为表现,并通过实例演示来加深理解。

一、引言

Spring AOP通过代理机制实现了对目标对象的非侵入式增强,即在不修改原有代码的基础上增加新的行为。这种能力主要依赖于切面的定义,而切面中的通知(Advice)则是实现增强的关键。在Spring AOP中,@Before@Around作为两种不同类型的通知,各自扮演着独特的角色。

  • @Before:在目标方法执行之前执行,主要用于执行一些前置条件检查、资源准备等工作。它不能阻止目标方法的执行,也无法修改目标方法的返回值或抛出的异常。
  • @Around:能够在目标方法执行前后执行代码,并且可以决定是否继续执行目标方法、修改其返回值或处理其抛出的异常。@Around通知提供了最灵活的通知类型,但相应地,其实现也较为复杂。

二、@Before与@Around的优先级探讨

在Spring AOP中,当同一个切面内同时定义了@Before@Around通知,并且它们都对同一个连接点(JoinPoint)感兴趣时,了解它们的执行顺序就变得尤为重要。实际上,@Around通知在@Before通知之前获得控制权,但请注意,这并不意味着@Around中的代码会先于@Before中的代码执行。

2.1 执行流程解析
  1. 切点匹配:首先,Spring AOP会根据切点表达式(Pointcut Expression)来确定哪些方法(连接点)需要被增强。如果@Before@Around通知的切点表达式都匹配了同一个方法,则这两个通知都会被激活。

  2. @Around通知介入:当方法被调用时,@Around通知首先介入。它会在目标方法执行前后编织代码,形成一个包裹着目标方法调用的“环绕”。

  3. @Before通知执行:虽然@Around通知首先获得控制权,但它会在自己的逻辑中显式地调用ProceedingJoinPoint.proceed()来执行目标方法。在调用proceed()之前,@Around通知有机会执行一些前置逻辑,这看起来像是@Before通知的执行时机。然而,重要的是要理解,这些前置逻辑仍然是@Around通知的一部分,而不是@Before通知。

  4. 目标方法执行ProceedingJoinPoint.proceed()的调用会触发目标方法的执行。

  5. @Around通知的后置逻辑:目标方法执行完毕后,控制流回到@Around通知中,此时@Around可以执行一些后置逻辑,如处理返回值、异常等。

  6. @Before通知不参与后续处理:值得注意的是,@Before通知仅在目标方法执行前执行其定义的代码,对于目标方法执行后的逻辑(如返回值处理、异常处理等),它并不参与。

2.2 示例演示

为了更好地说明这一点,我们通过一个简单的示例来演示@Before@Around通知的执行流程。

  1. @Aspect
  2. @Component
  3. public class MyAspect {
  4. @Before("execution(* com.example.service.*.*(..))")
  5. public void beforeAdvice(JoinPoint joinPoint) {
  6. System.out.println("Before advice executed for: " + joinPoint.getSignature().getName());
  7. }
  8. @Around("execution(* com.example.service.*.*(..))")
  9. public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
  10. System.out.println("Around advice before proceeding: " + proceedingJoinPoint.getSignature().getName());
  11. Object result = proceedingJoinPoint.proceed(); // This is where the target method is actually executed
  12. System.out.println("Around advice after proceeding: " + proceedingJoinPoint.getSignature().getName());
  13. return result;
  14. }
  15. }
  16. @Service
  17. public class MyService {
  18. public void myMethod() {
  19. System.out.println("Executing myMethod");
  20. }
  21. }

在上述示例中,当MyServicemyMethod方法被调用时,输出顺序将是:

  1. Around advice before proceeding: myMethod
  2. Before advice executed for: myMethod
  3. Executing myMethod
  4. Around advice after proceeding: myMethod

这个输出清晰地展示了@Around通知在@Before通知之前获得控制权,并在目标方法执行前后执行了额外的逻辑。

三、深入理解

虽然@Around通知在@Before通知之前获得控制权,但我们必须清楚地认识到,@Around通知中的前置逻辑和@Before通知的逻辑在逻辑上是有区别的。@Around的前置逻辑是环绕通知的一部分,它可以决定是否继续执行目标方法;而@Before通知则仅用于在目标方法执行前执行一些前置操作,无法控制目标方法的执行。

此外,@Around通知的灵活性也意味着更高的复杂性。在编写@Around通知时,需要特别小心以确保不会无意中引入错误,比如忘记调用proceed()方法,这将导致目标方法不会被执行。

四、结论

通过本章的探讨,我们深入理解了Spring AOP中@Before@Around通知的执行顺序及其背后的机制。虽然@Around通知在逻辑上先于@Before通知获得控制权,但@Before通知的代码实际上是在@Around通知的某个特定阶段(即调用proceed()之前)执行的。这种执行顺序的理解对于编写高效、可维护的Spring AOP切面至关重要。在实际开发中,应根据具体需求选择合适的通知类型,并注意控制通知的复杂性和潜在风险。


该分类下的相关小册推荐: