skip to content

What is @After advice in Spring AOP and when does it run?

level: juniorimportance: must knowfreq 58%

answer

  1. Runs like finally — success or exception
  2. No returning / no throwing binding
  3. JoinPoint yes, return value no
  4. Cleanup / resource release
  5. Exception still propagates after it runs

basics

~20 s

@After is advice that runs after a matched method finishes — whether it returned normally or threw an exception. It behaves like a finally block, so it is used for cleanup. It cannot see the return value or the exception.

solid answer

~40 s

@After (the 'after finally' advice) is one of Spring AOP's five advice types. You put @After on an aspect method with a pointcut, e.g. @After("execution(* com.acme.service..*(..))"). Spring wraps the target bean in a proxy; when a matched method completes, the @After method always runs — on both the normal-return path and the exception path — mirroring a try/finally block. Its typical uses are cleanup and resource release: unlocking, closing handles, clearing a ThreadLocal or MDC context, decrementing a counter. It can declare a JoinPoint parameter to inspect the signature and arguments, but unlike @AfterReturning it has no 'returning' binding and unlike @AfterThrowing it has no 'throwing' binding — so it cannot access the returned value or the thrown exception. If the method threw, @After runs and then the exception keeps propagating.

code

java · 12 lines
java
@Aspect
@Component
public class CleanupAspect {

    @After("execution(* com.acme.service.OrderService.*(..))")
    public void afterAnyServiceCall(JoinPoint joinPoint) {
        // Always runs: normal return OR exception thrown.
        MDC.clear(); // e.g. clear per-request logging context
        System.out.println("Finished: " + joinPoint.getSignature().toShortString());
        // Cannot read the return value or the exception here.
    }
}

go deeper

for a junior

Must know: @After = runs after, no matter what, like finally; used for cleanup.

for a middle

Should also state it cannot bind the return value or exception, and that it does not suppress exceptions.

for a senior

Frame it against the other four advice types and note proxy/self-invocation limits.

for a principal

Discuss when finally-style cleanup belongs in an aspect vs. the method itself, and idempotency of cleanup on the throwing path.

### What AOP and advice are Aspect-Oriented Programming (AOP) lets you factor out cross-cutting concerns (logging, security, cleanup) into an **aspect** instead of scattering them through business code. An aspect contains **advice** — code that runs at chosen points in program execution. The chosen points are selected by a **pointcut** (an expression like `execution(* com.acme..*(..))`). A single point where advice can run (here, a method call) is a **join point**. Spring implements this with **proxies**: when a bean is matched by a pointcut, Spring replaces it with a proxy (a JDK dynamic proxy if the bean implements an interface, otherwise a CGLIB subclass). Calls go through the proxy, which invokes the advice around the real method. ### The five advice types - `@Before` — runs before the method. - `@AfterReturning` — runs only after a **normal** return; can bind the return value via `returning`. - `@AfterThrowing` — runs only if the method **throws**; can bind the exception via `throwing`. - `@After` — runs **after the method regardless of outcome** (normal or exception). This is the topic here. - `@Around` — wraps the method; must call `ProceedingJoinPoint.proceed()` and can do work before and after, including its own try/finally. ### @After semantics (finally) `@After` is called the *after (finally) advice* in the Spring reference. It always executes once the join point completes, in exactly the situations a Java `finally` block would run. So for: ``` try { result = method(); } finally { /* @After runs here */ } ``` it runs whether `method()` returns or throws. **What it can and cannot access:** - It **may** declare a `JoinPoint` as its first parameter to read the method signature (`joinPoint.getSignature()`) and arguments (`joinPoint.getArgs()`). - It **cannot** access the return value — there is no `returning` attribute on `@After`. - It **cannot** access the exception — there is no `throwing` attribute on `@After`. If you need either of those, use `@AfterReturning`/`@AfterThrowing`, or use `@Around` with a `try/finally`. ### Exception behavior `@After` does **not** swallow or suppress the exception. On the throwing path Spring runs `@After` and then re-propagates the original exception to the caller. `@After` running is not a `catch`. ### Enabling it You need `spring-aop` (and normally `aspectjweaver` for the annotation pointcut language), the class annotated with `@Aspect` and registered as a Spring bean (e.g. `@Component`), and AspectJ auto-proxying enabled via `@EnableAspectJAutoProxy` (Spring Boot autoconfigures this when AspectJ is on the classpath). ### Gotchas - **Proxy self-invocation**: if the target calls its own `@After`-matched method internally (`this.foo()`), the call does not go through the proxy, so no advice fires. - **Only proxied beans**: advice applies to Spring-managed beans reached through the proxy, not to `new`-ed objects. - **Ordering vs @AfterReturning/@AfterThrowing** within the same aspect changed in Spring 5.2.7 (covered in the advanced question). ### When to use Reach for `@After` when the cleanup is identical on success and failure and does not need the result/exception — releasing a lock, clearing context, closing something. If the cleanup differs by outcome, or you need the value/exception, prefer the more specific advice or `@Around`.

  • If the advised method throws, does @After stop the exception from reaching the caller?
    No. @After is not a catch block. Spring runs the @After method and then re-propagates the original exception. Only @Around (which can catch inside proceed()) or a real try/catch can suppress it.
  • How would you inspect which method and arguments triggered the @After advice?
    Declare a JoinPoint as the first parameter and call joinPoint.getSignature() and joinPoint.getArgs(). That is the only binding @After supports — there is no returning/throwing attribute.

context