skip to content

What is @Around advice in Spring AOP, and why must it call ProceedingJoinPoint.proceed()?

level: juniorimportance: must knowfreq 78%

answer

  1. wraps the join point
  2. proceed() runs the target
  3. must return the result
  4. throws Throwable
  5. skip proceed = short-circuit

basics

~20 s

@Around wraps a method call. It runs code before and after the target method. You must call proceed() to actually run the target method; if you skip it, the target never executes and you return your own value instead.

solid answer

~40 s

@Around is the most powerful AOP advice type: it fully surrounds the intercepted method (the 'join point'). Its method receives a ProceedingJoinPoint, and calling proceed() executes the target method, returning its result. Because @Around wraps execution, you control everything: run code before and after, decide whether to call proceed() at all (short-circuit), modify the return value, pass different arguments, or catch/translate exceptions. If you never call proceed(), the target is skipped and your returned object becomes the caller's result. You must also return the (possibly transformed) result — unlike @Before/@After, @Around is responsible for returning a value. It's used for timing, caching, retry, transaction-like wrapping, and logging. Its return type is typically Object, and proceed() declares throws Throwable.

code

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

    @Around("execution(* com.example.service.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("Before: " + pjp.getSignature());
        Object result = pjp.proceed(); // invoke the real method
        System.out.println("After: returned " + result);
        return result;                 // hand the result back to the caller
    }
}

go deeper

for a junior

Know that @Around wraps the method and proceed() runs the real method; forgetting proceed() skips it.

for a middle

Explain returning the result, throws Throwable, and short-circuiting by skipping proceed().

for a senior

Contrast @Around with the narrower advice types and explain when the extra power is justified.

for a principal

Frame @Around's power vs risk (dropped returns, proxy self-invocation) and default to narrower advice unless wrapping is required.

## What AOP and a 'join point' are **Aspect-Oriented Programming (AOP)** lets you inject cross-cutting behavior (logging, timing, security, caching) around your business methods without editing them. In Spring, this is done with a **proxy**: Spring wraps your bean in a generated object that intercepts calls and runs 'advice' around them. A **join point** is a point in program execution where advice can run — in Spring AOP, always a **method execution** on a Spring bean. A **pointcut** is an expression selecting which join points to advise. **Advice** is the code that runs. ## The five advice types - `@Before` — runs before the method. - `@AfterReturning` — runs after normal return. - `@AfterThrowing` — runs if the method throws. - `@After` — runs no matter what (finally-style). - `@Around` — **wraps** the method; the most powerful, and the only one that can prevent the method from running or change its result. ## @Around anatomy ```java @Aspect @Component public class TimingAspect { @Around("execution(* com.example.service..*(..))") public Object time(ProceedingJoinPoint pjp) throws Throwable { long start = System.nanoTime(); Object result = pjp.proceed(); // runs the target method long took = System.nanoTime() - start; // ... log took ... return result; // MUST return the result } } ``` Key rules: 1. The **first parameter must be `ProceedingJoinPoint`** (a subtype of the ordinary `JoinPoint`). It's the handle to the target method. 2. **`proceed()` invokes the target method** and returns whatever the target returned (as `Object`). It is declared `throws Throwable`, so your advice method must declare `throws Throwable` (or handle it). 3. **Your advice's return value becomes the caller's return value.** If you forget to return `pjp.proceed()`'s result, the caller silently gets `null`. 4. The advice method's return type is normally `Object`. ## What proceed() lets you do - **Run before/after logic** by placing code around the `proceed()` call. - **Short-circuit**: don't call `proceed()` at all — the target never runs, and you return a substitute value (the basis of caching). - **Modify arguments**: `pjp.proceed(newArgsArray)` calls the target with different arguments. - **Alter the return value**: transform what `proceed()` returned before returning it. - **Handle exceptions**: wrap `proceed()` in try/catch to translate, suppress, retry, or add context. ## Common gotchas - **Forgetting to return the result** → callers get `null`. Most frequent bug. - **Forgetting `throws Throwable`** or swallowing exceptions unintentionally. - **Not calling proceed()** when you meant to → the real method never runs. - `@Around` sees `void` methods too — `proceed()` returns `null` for them, and you still return it. - Only works through the **proxy**: an internal call within the same bean (`this.method()`) bypasses the proxy, so the advice doesn't fire (self-invocation limitation). ## When to use Use `@Around` when you need to **decide whether/how the method runs** or **change its result** — timing, caching, retry, rate-limiting, argument sanitization. For pure side effects (audit logging on success, cleanup), prefer the narrower `@Before`/`@After*` types, which are simpler and can't accidentally drop the return value.

  • What happens if your @Around method never calls proceed()?
    The target method is never executed. Whatever your advice returns becomes the caller's result (or null if you return nothing). This is exactly how a cache-hit short-circuit works.
  • What happens if you call proceed() but forget to return its result?
    The target runs, but your advice returns null (or your own value), so the real return value is lost and the caller sees null. It's a very common bug.

saying these in an interview costs you the question

  • Thinking @Around runs only before the method like @Before
  • Believing proceed() is optional boilerplate you can omit
  • Not knowing the advice must return the result to the caller
  • Saying @After can change the return value (only @Around can)

context