skip to content

Within a single @Aspect, in what order do @After, @AfterReturning, and @AfterThrowing run, and how has that changed?

level: principalimportance: nice to knowfreq 18%

answer

  1. 5.2.7 aligned within-aspect order to AspectJ
  2. Precedence: Around>Before>After>AfterReturning>AfterThrowing
  3. Exit: returning/throwing first, @After last
  4. Same-type same-join-point = undefined
  5. Cross-aspect: @Order / Ordered, lower=outer

basics

~10 s

Since Spring 5.2.7, within one aspect @After runs after @AfterReturning/@AfterThrowing on the way out (finally-last, matching AspectJ). Before 5.2.7 the order was less consistent, often the reverse. Across different aspects, use @Order/Ordered.

solid answer

~40 s

Spring assigns advice within a single @Aspect a fixed precedence: @Around and @Before highest, then @After, then @AfterReturning/@AfterThrowing. Higher precedence means outermost — it runs first entering and last leaving. So on exit @AfterReturning or @AfterThrowing runs first and @After runs last, giving true finally-last behavior aligned with AspectJ. This was standardized in Spring Framework 5.2.7; earlier versions had inconsistent, sometimes reversed ordering where @After ran before the returning/throwing advice — a well-known upgrade gotcha. When two advice methods of the same type in the same aspect share a join point, the order is undefined; combine them or split aspects. Ordering between different aspects is separate: implement Ordered or annotate the aspect with @Order — lower value = higher precedence, and the same outer-first-in/last-out nesting applies.

code

java · 18 lines
java
// Spring 5.2.7+ within ONE aspect, on a normal return, execution order is:
//   @Around (before proceed) -> @Before -> METHOD -> @AfterReturning -> @After -> @Around (after proceed)
@Aspect
@Component
@Order(0) // cross-aspect precedence; lower value = outermost
public class TxLikeAspect {

    @Around("execution(* com.acme.Svc.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        try { return pjp.proceed(); } finally { /* outermost finally */ }
    }

    @AfterReturning(pointcut = "execution(* com.acme.Svc.*(..))", returning = "r")
    public void afterReturning(Object r) { /* runs before @After */ }

    @After("execution(* com.acme.Svc.*(..))")
    public void after() { /* finally-last: runs after @AfterReturning */ }
}

go deeper

for a junior

Just know @After runs after the method; ordering nuance is beyond scope.

for a middle

Know cross-aspect ordering uses @Order/Ordered (lower = outer).

for a senior

Explain the within-aspect precedence and that @After is finally-last on 5.2.7+.

for a principal

Discuss the 5.2.7 alignment with AspectJ, the pre-5.2.7 gotcha, undefined same-type ordering, and preferring a single @Around when sequence is load-bearing.

### Two independent ordering questions 1. **Between different aspects** hitting the same join point. 2. **Between advice methods inside one @Aspect**. They are governed by different rules; conflating them is a common mistake. ### Between different aspects Each aspect has a precedence. Set it by implementing `org.springframework.core.Ordered` or annotating the aspect class with `@Order`. **Lower value = higher precedence = outermost.** The outermost aspect's `@Before` runs first and its 'after' advice runs last — classic nesting, like Russian dolls. If you do not set an order, the relative order of two independent aspects is **undefined**. ### Inside one @Aspect (the heart of this question) Since **Spring Framework 5.2.7**, advice types in the *same* aspect are ordered by a fixed precedence, from highest to lowest: `@Around` > `@Before` > `@After` > `@AfterReturning` > `@AfterThrowing`. Apply the outer-first-in / outer-last-out nesting to that list: - **Entering**: `@Around` (before `proceed()`), then `@Before`. - **Leaving**: lowest precedence runs first, highest last. So `@AfterThrowing`/`@AfterReturning` (lowest) run **first**, then `@After`, then the code after `proceed()` in `@Around`. Concretely, on a normal return: `@AfterReturning` runs, then `@After`. On an exception: `@AfterThrowing` runs, then `@After`, then the exception propagates. This makes `@After` genuinely *finally-last* — it always closes out after the outcome-specific advice — matching AspectJ semantics. ### The historical change (why interviewers ask) Before 5.2.7, Spring's within-aspect ordering was not aligned with AspectJ and was effectively based on other heuristics; a frequently reported symptom was `@After` executing **before** `@AfterReturning`, the opposite of finally intuition. Teams that relied on ordering (e.g. `@AfterReturning` commits/audits the result, then `@After` clears context) could see behavior change on upgrade. The 5.2.7 release deliberately standardized the order to the precedence list above. On any modern Spring Boot (2.3.1+/Spring 5.2.7+) you get the finally-last behavior. ### Same-type collisions If two `@After` methods (or two `@Before`, etc.) in the **same** aspect match the **same** join point, their relative order is **undefined** and `@Order` on methods does not help. The reference guidance is to collapse them into one advice method or move them into separate aspects ordered with `@Order`. ### Design implications - Don't build correctness on within-aspect ordering across advice **types** unless you are on 5.2.7+ and have verified the precedence list; even then, prefer a single `@Around` when the sequence is load-bearing, because it makes the order explicit in code. - For cross-aspect sequencing (security before tx before audit), always assign `@Order` explicitly rather than relying on discovery order. - Remember `@After` runs after `@AfterReturning` — if `@AfterReturning` mutates shared state the `@After` cleanup depends on, the direction is now well-defined but was not pre-5.2.7. ### Quick reference Entering: Around(pre) -> Before -> [method] . Leaving: AfterReturning|AfterThrowing -> After -> Around(post).

  • Two @After methods in the same aspect match the same method. Can you force one to run before the other?
    Not reliably. Same-type advice at the same join point within one aspect has undefined order, and @Order on methods is ignored. Merge them into one advice method, or split into two aspects ordered with @Order/Ordered.
  • How do you make aspect A's advice always wrap aspect B's at the same join point?
    Give A a lower @Order (or Ordered value) than B. Lower value = higher precedence = outermost, so A's @Before runs first and its after-advice runs last, nesting B inside it.
  • On Spring 5.2.7+, on a normal return does @After or @AfterReturning run first?
    @AfterReturning runs first, then @After (finally-last). Before 5.2.7 the order could be reversed, which was a known upgrade gotcha.

saying these in an interview costs you the question

  • Claiming @After always runs before @AfterReturning on modern Spring
  • Using @Order on advice methods to sequence same-type advice in one aspect
  • Assuming two unordered aspects have a defined, stable execution order
  • Thinking within-aspect ordering never changed across Spring versions

context