How do you modify method arguments from an @Around advice, and why doesn't editing getArgs() work?
answer
- proceed(args) substitutes inputs
- no-arg proceed() = original args
- getArgs() is read/inspection only
- array must match count + types positionally
- only in @Around
basics
~20 sCall proceed(Object[] args) with a new argument array — that array is what the target actually receives. Editing the array from getArgs() alone does not change the real call; you must pass your modified array into proceed(args).
solid answer
~40 sTo change what the target method sees, use the overload proceed(Object[] args) on ProceedingJoinPoint. Typical pattern: grab the current args with getArgs(), copy/modify the elements you need (e.g. trim a String, mask a value, clamp a number), then call proceed(modifiedArgs) and return its result. Just mutating the array returned by getArgs() is not enough on its own: the no-arg proceed() re-uses the original arguments, so unless you feed your array into proceed(args), the target runs with the untouched inputs. The replacement array must match the method's parameter count and types positionally, or you'll get an IllegalArgumentException / class-cast failure at invocation. This is only possible in @Around, since plain JoinPoint has no proceed(). It's the standard mechanism for input sanitization, normalization, defaulting, or redaction in a cross-cutting aspect.
code
java · 18 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 TrimmingAspect {
@Around("execution(* com.katajob.web..*(String, ..))")
public Object trimFirstString(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs(); // inspect current args
if (args.length > 0 && args[0] instanceof String s) {
args[0] = s.trim(); // modify a copy
}
return pjp.proceed(args); // substitute + continue
}
}go deeper
Know proceed(args) exists to change inputs.
Show the get-modify-proceed pattern and that no-arg proceed uses originals.
Explain the copy-array semantics and the positional count/type contract.
Discuss blast-radius risk of argument-rewriting aspects and scoping/testing discipline.
**Setup.** `ProceedingJoinPoint` (usable only in `@Around`) has two ways to continue the invocation: - `proceed()` — continues using the **original** arguments captured when the join point was reached. - `proceed(Object[] args)` — continues using the **array you supply** as the arguments. **Why editing getArgs() doesn't change the call.** `getArgs()` returns an `Object[]` you can read and even mutate, but calling the **no-arg** `proceed()` does not consult that array you touched — it uses the original arguments the framework already holds. So the only supported way to alter inputs is to build/modify an array and pass it explicitly to `proceed(args)`. Conceptually: `getArgs()` is *inspection*; `proceed(args)` is *substitution*. (In common AspectJ runtimes the array returned by `getArgs()` is a copy, which is another reason mutating it in place is unreliable.) **Correct pattern.** ``` Object[] args = pjp.getArgs(); if (args.length > 0 && args[0] instanceof String s) { args[0] = s.trim(); } return pjp.proceed(args); // MUST pass the array back in ``` **Positional contract and gotchas:** - The array length and element types must line up with the method's declared parameters, in order. A wrong count or an incompatible type causes an exception (e.g. `IllegalArgumentException` / argument-binding failure) when the invocation is dispatched. - Primitives are auto-boxed in the `Object[]` — putting `null` where a primitive `int` is expected will blow up. - Don't confuse this with pointcut argument binding (`args()`): that binds named parameters into the advice for reading; `proceed(args)` is what actually changes the downstream call. - Only `@Around` can do this. `@Before` can read arguments but cannot substitute them (no proceed()). **When to use.** Cross-cutting input normalization: trimming/lowercasing strings, masking PII before it reaches the method, injecting defaults for null inputs, clamping numeric ranges, or unwrapping/wrapping DTOs. Because it's powerful and easy to get subtly wrong (silent behavior changes across many methods), keep such aspects narrowly pointcut-scoped and well-tested. **Return value still matters.** As always in `@Around`, return the result of `proceed(args)`; discarding it hands the caller the wrong value.
- What happens if the array passed to proceed(args) has the wrong number of elements?The invocation fails at dispatch with an argument-binding exception (e.g. IllegalArgumentException); the array must match the target's parameter count and types positionally.
- Can @Before advice modify the arguments a method receives?No. @Before gets a plain JoinPoint with no proceed(); it can read getArgs() but cannot substitute the actual arguments. Only @Around via proceed(args) can.
saying these in an interview costs you the question
- Believing mutating getArgs()'s array changes the real call without proceed(args)
- Passing a differently-sized array to proceed(args)
- Trying to change arguments from @Before/@After
- Forgetting to return proceed(args)'s result