How do you modify the arguments passed to the target method from an @Around advice, and what are the rules of proceed(Object[])?
answer
- proceed() vs proceed(Object[])
- getArgs() = snapshot copy
- same length + order + types
- mutating getArgs() alone = no-op
- only @Around can replace args
basics
~10 sCall the overloaded proceed(Object[] args) instead of proceed(). Get the current args with pjp.getArgs(), change them in a new array, and pass that array. The array length and types must match the method's parameters.
solid answer
~40 sProceedingJoinPoint has two forms: proceed() reuses the original arguments, and proceed(Object[] args) invokes the target with a replacement argument array. To modify args, read pjp.getArgs() (a copy of the current arguments), build a new Object[] with your changes, and call pjp.proceed(newArgs). The array must have the same length and element order as the method's parameters, and each element must be assignment-compatible with the corresponding parameter type — a mismatch throws at invocation. Note getArgs() returns a snapshot: mutating the returned array does not affect the call unless you pass it to proceed(Object[]). This pattern is used for sanitizing/normalizing inputs, masking sensitive parameters, injecting defaults, or trimming strings. It only works with @Around — the other advice types can read arguments (JoinPoint.getArgs()) but cannot replace them.
code
java · 15 lines@Aspect
@Component
public class TrimAspect {
@Around("execution(* com.example.UserService.register(..))")
public Object trimStrings(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs(); // copy of current args
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof String s) {
args[i] = s.trim();
}
}
return pjp.proceed(args); // must pass array back
}
}go deeper
Know proceed() has a form that accepts a replacement argument array.
Explain getArgs() is a snapshot, and proceed(Object[]) enforces length/order/type matching.
Discuss args() pointcut binding for type-safe reads and immutability concerns when rewriting.
Weigh the maintainability cost of silent argument rewriting across a codebase and prefer explicit, narrowly-scoped pointcuts.
## Two overloads of proceed() `ProceedingJoinPoint` (the special join point passed only to `@Around`) offers: - **`Object proceed()`** — runs the target with the **original arguments** that the caller supplied. - **`Object proceed(Object[] args)`** — runs the target with a **replacement argument array** you provide. Both return the target's result as `Object` and declare `throws Throwable`. ## Reading the current arguments `JoinPoint.getArgs()` returns an `Object[]` holding the current arguments in **declaration order**. This is a **snapshot/copy of the references** — reassigning elements of this returned array does **not** change what the target receives. You must feed the modified array to `proceed(Object[])` for it to take effect. ```java @Around("execution(* com.example.OrderService.place(..))") public Object normalize(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); if (args.length > 0 && args[0] instanceof String s) { args[0] = s.trim(); // sanitize the first argument } return pjp.proceed(args); // invoke with modified args } ``` ## Rules and constraints of proceed(Object[]) 1. **Length must match** the method's parameter count. Passing the wrong number of elements results in an `IllegalArgumentException`/reflection error at proceed time. 2. **Order matters** — element `i` maps to parameter `i`. 3. **Type compatibility** — each element must be assignment-compatible with the declared parameter type (autoboxing applies for primitives). A `String` where an `Integer` is expected fails at invocation. 4. **Passing `null`** is allowed for reference-type parameters but will NPE if the target dereferences it, and is illegal for a primitive parameter. 5. The change is invisible to the caller — the caller's variables are unaffected; only what the **target** sees changes. ## AspectJ note (binding args in the pointcut) Instead of index juggling, you can bind arguments by name using `args()`: ```java @Around("execution(* place(..)) && args(orderId,..)") public Object around(ProceedingJoinPoint pjp, String orderId) throws Throwable { // orderId is bound; still call proceed(new Object[]{...}) to change it return pjp.proceed(new Object[]{ orderId.trim() }); } ``` Binding gives type-safe access for reading, but to **change** an argument you still call `proceed(Object[])` with the full, correctly-ordered array. ## Gotchas - **Mutating getArgs() without passing it to proceed(Object[])** does nothing — a classic silent no-op. - **Wrong array length/type** blows up only at runtime, at the proceed call. - The other advice types (`@Before`, `@After*`) can read args but **cannot** substitute them — only `@Around` can. - For an immutable argument object you'd construct a new instance and place it in the array; you cannot 'edit in place' a value the caller still holds a reference to without side effects. ## When to use Argument rewriting is good for **normalization** (trim/lowercase), **defaulting** (fill nulls), **masking** before delegating, or **decorating** collections. Keep it targeted — silently changing arguments across many methods makes behavior hard to reason about.
- If you call pjp.getArgs(), mutate an element, but then call the no-arg proceed(), what happens?The target runs with the ORIGINAL arguments. The no-arg proceed() ignores your mutated array; you must call proceed(Object[]) with the modified array for changes to take effect.
- What error do you get if the array passed to proceed has the wrong length or types?It fails at invocation time (runtime) with an IllegalArgumentException/reflection-style error — the framework maps the array positionally onto the target's parameters, so a mismatch throws.
saying these in an interview costs you the question
- Thinking mutating getArgs() alone changes what the target receives
- Believing @Before or @After can replace arguments
- Ignoring array length/order/type constraints of proceed(Object[])
- Assuming the caller's own variables change when you rewrite args