How do you write around advice in schema-based AOP, and what must the advice method's signature look like?
answer
- <aop:around> method: first param ProceedingJoinPoint
- must call pjp.proceed(); return value = caller's result
- throws Throwable; return type Object
- proceed(args[]) substitutes arguments
- forgetting proceed() => target never runs (silent bug)
basics
~10 sUse <aop:around pointcut="..." method="..."/>. The referenced POJO method must take a ProceedingJoinPoint as its first parameter and call proceed() to run the target. Its return value becomes the method's result.
solid answer
~40 sAround advice is declared with <aop:around pointcut-ref="..." method="..."/> inside an <aop:aspect>. The backing method's first parameter must be org.aspectj.lang.ProceedingJoinPoint; inside it you call pjp.proceed() to invoke the target, optionally pjp.proceed(newArgs) to substitute arguments. Whatever the method returns is what the caller receives, so around advice can short-circuit (skip proceed entirely), transform the result, catch or rethrow exceptions, and wrap the call in timing/retry/caching logic. The method's declared return type should be Object (or compatible) and it should declare throws Throwable since proceed() propagates the target's checked exceptions. Additional bound parameters from args() can follow the ProceedingJoinPoint. Forgetting to call proceed() silently prevents the target method from ever executing — a classic bug.
code
java · 16 linespackage com.app;
import org.aspectj.lang.ProceedingJoinPoint;
public class RetryAspect {
// <aop:around pointcut-ref="remoteCalls" method="retry"/>
public Object retry(ProceedingJoinPoint pjp) throws Throwable {
int attempts = 0;
while (true) {
try {
return pjp.proceed(); // run target; return its result
} catch (RuntimeException ex) {
if (++attempts >= 3) throw ex; // give up after 3 tries
}
}
}
}go deeper
Know around advice uses ProceedingJoinPoint and must call proceed().
Explain the full signature (Object return, throws Throwable), returning the result, and proceed(args) for substitution.
Discuss short-circuit/caching/retry patterns, precedence relative to other advice, and the forgotten-proceed and double-proceed bugs.
Reason about idempotency of retry loops, exception translation policies, and interaction of around advice ordering with transactional advisors.
**What around advice is.** Around advice is the most powerful advice type: it *surrounds* the target method call, receiving control before and after, and deciding whether the target runs at all. It's the only advice that can prevent execution, alter arguments, change or replace the return value, and swallow or translate exceptions — all in one place. Use it for timing, caching, retries, transactions-like wrapping, and argument/return transformation. **XML declaration.** Inside an `<aop:aspect ref="bean">`: ```xml <aop:around pointcut-ref="serviceOps" method="time"/> ``` There's nothing special in the XML compared to other advice — the power comes from the method signature it points at. **Required signature.** The `method` on the POJO must declare, as its **first parameter**, `org.aspectj.lang.ProceedingJoinPoint`. This is a subtype of `JoinPoint` that adds `proceed()`: ```java public Object time(ProceedingJoinPoint pjp) throws Throwable { long t0 = System.nanoTime(); try { return pjp.proceed(); // runs the target method } finally { log(System.nanoTime() - t0); } } ``` - **Return type** should be `Object` (or a type assignable from the real return). Whatever you return is handed back to the caller — you can return `pjp.proceed()` unchanged, transform it, or return something entirely different. - **`throws Throwable`** — `proceed()` propagates whatever the target throws (including checked exceptions), so the advice method must allow it. - **`proceed()` variants:** `pjp.proceed()` runs the target with the original arguments; `pjp.proceed(Object[] newArgs)` runs it with *substituted* arguments (the array length/order must match the target signature). **Binding extra parameters.** After the `ProceedingJoinPoint`, you can bind arguments captured by `args(...)`: ```xml <aop:around pointcut="execution(* charge(..)) and args(amount)" method="cap" arg-names="amount"/> ``` ```java public Object cap(ProceedingJoinPoint pjp, BigDecimal amount) throws Throwable { ... } ``` **What you can do inside:** - **Short-circuit:** return a cached value *without* calling `proceed()` — the target never runs. - **Retry:** loop, calling `proceed()` again on failure. - **Transform args:** `pjp.proceed(new Object[]{sanitized})`. - **Transform result:** `Object r = pjp.proceed(); return decorate(r);`. - **Exception handling:** catch from `proceed()`, translate, or rethrow. **Gotchas.** - **Forgetting `proceed()`** is the classic bug: the target method silently never executes and the caller gets whatever (often `null`) the advice returns. - **Calling `proceed()` more than once** re-invokes the target — fine for retry, dangerous if unintended (double side effects). - **Return-type mismatch:** if the advice returns something incompatible with the caller's expected type, you get a `ClassCastException` at the call site. - Around advice has the **highest precedence granularity** — it wraps all the 'lower' advice; a before advice in the same aspect logically runs after the around's pre-`proceed()` code and before the target, depending on ordering. - Standard Spring AOP limits still apply (proxy-based, method-execution join points, self-invocation bypass).
- What happens if around advice never calls proceed()?The target method is never invoked. The caller receives whatever the advice returns (often null), and none of the target's side effects occur — a subtle, common bug.
- How do you change the arguments passed to the target from around advice?Call pjp.proceed(Object[] newArgs) with a replacement array matching the target's parameter count and order; the target executes with those substituted arguments.
saying these in an interview costs you the question
- Using JoinPoint instead of ProceedingJoinPoint for around advice (only ProceedingJoinPoint has proceed()).
- Assuming the target still runs even if you don't call proceed().
- Declaring the advice void or not returning proceed()'s result, thereby dropping the real return value.