How does ProceedingJoinPoint differ from JoinPoint, and where can you use it?
answer
- extends JoinPoint + proceed()
- only in @Around
- proceed() = continue the chain
- return type Object, throws Throwable
- forget to return proceed() = null/broken
basics
~20 sProceedingJoinPoint extends JoinPoint and adds proceed(), which actually invokes the advised method (or the next advice in the chain). It is valid only inside @Around advice. Other advice types get the plain JoinPoint, which cannot proceed.
solid answer
~40 sProceedingJoinPoint is a subtype of JoinPoint usable only in @Around advice. On top of the inspection methods (getArgs, getSignature, getTarget, getThis) it adds proceed() and proceed(Object[] args), which continue the invocation down the interceptor chain to the next advice or, ultimately, the real target method. This makes @Around the only advice that can decide whether the target runs at all: call proceed() to run it, skip it to short-circuit (caching, security), call it multiple times (retry), wrap it in timing, or transform its return value. proceed() returns the invocation result as Object and declares throws Throwable, so your @Around method typically returns Object and declares throws Throwable too. The other advice kinds run at a fixed point and never get proceed() — they can only observe, not control, the call.
code
java · 20 linesimport org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TimingAspect {
@Around("@annotation(com.katajob.Timed)")
public Object time(ProceedingJoinPoint pjp) throws Throwable { // must be 1st param
long start = System.nanoTime();
try {
return pjp.proceed(); // run the target; MUST return its result
} finally {
long ms = (System.nanoTime() - start) / 1_000_000;
System.out.println(pjp.getSignature().getName() + " took " + ms + " ms");
}
}
}go deeper
Know proceed() runs the method and lives only in @Around.
Explain the return-Object/throws-Throwable contract and short-circuit use cases.
Discuss chain semantics (next advice vs target) and the must-return-proceed gotcha.
Weigh @Around vs simpler advice on robustness and reason about interceptor-chain ordering.
**Recap.** A *join point* in Spring AOP is a method execution on a proxied bean. Advice is code attached to it. `org.aspectj.lang.JoinPoint` is the read-only handle to that execution given to `@Before`/`@After`/`@AfterReturning`/`@AfterThrowing`. **`ProceedingJoinPoint`.** `org.aspectj.lang.ProceedingJoinPoint` **extends** `JoinPoint`, so it has every inspection method (`getArgs`, `getSignature`, `getTarget`, `getThis`) **plus** two control methods: - **`Object proceed() throws Throwable`** — continues execution down the AOP *interceptor chain*: the next advice in order, and eventually the real target method. Returns whatever that downstream call returns. - **`Object proceed(Object[] args) throws Throwable`** — same, but you supply a replacement argument array, letting you rewrite the inputs the downstream call sees. **Why only `@Around`.** `@Around` is the only advice kind that *wraps* the join point rather than running at a fixed moment. Spring injects a `ProceedingJoinPoint` as the **first parameter** of an `@Around` method precisely so the advice can decide the fate of the call. The other advice kinds are positioned by Spring (before, after-returning, after-throwing, finally) and never surrender control of the invocation to your code, so they receive only `JoinPoint`. Declaring `ProceedingJoinPoint` in a non-`@Around` advice is a configuration error. **The control power of proceed():** - **Run normally:** `return pjp.proceed();` - **Short-circuit:** return a cached/authorized value *without* calling proceed() — the target never runs. - **Retry:** call proceed() in a loop until it succeeds. - **Around timing/metrics:** record `System.nanoTime()` before and after proceed(). - **Transform result:** call proceed(), then modify or wrap the returned Object. - **Rewrite inputs:** call proceed(newArgs) to change arguments (e.g. trimming/sanitizing). **Return-value and exception contract.** proceed() returns `Object` (the downstream result) and declares `throws Throwable`. Therefore an `@Around` method usually has return type `Object` and `throws Throwable`. **Critical gotcha:** whatever proceed() returns you must return from your advice — if you call proceed() but return something else (or forget to return it), the real caller gets the wrong value or `null`. Similarly, if you never call proceed(), the target method silently does not execute. **Signature shape.** A canonical `@Around` looks like `Object aroundX(ProceedingJoinPoint pjp) throws Throwable { ... return pjp.proceed(); }`. **When to reach for it.** Use `@Around` + `ProceedingJoinPoint` when you must *control* the call: caching, transactions-like wrapping, retries, circuit breaking, rate limiting, timing, argument sanitization, return-value transformation. If you only need to *observe* (log/audit/metrics on entry or exit), prefer a simpler advice with plain `JoinPoint` — it can't accidentally break the call by forgetting to proceed().
- What happens if an @Around advice never calls proceed()?The target method (and any downstream advice) never executes. The advice's return value becomes the call result — useful for caching/security short-circuits, but a bug if unintended.
- Why does an @Around method typically declare 'throws Throwable' and return Object?Because proceed() is declared to return Object and throw Throwable; matching those lets exceptions propagate naturally and passes the downstream return value through unchanged.
saying these in an interview costs you the question
- Using ProceedingJoinPoint in @Before/@After
- Calling proceed() but not returning its result
- Assuming @Around runs the target automatically without proceed()
- Thinking JoinPoint has a proceed() method