skip to content

How do you access the method arguments and signature inside @Before advice, and can you modify them?

level: middleimportance: should knowfreq 55%

answer

  1. First param = JoinPoint, auto-injected
  2. getArgs / getSignature / getTarget / getThis
  3. args() binds typed params by name
  4. Rebinding args is ignored — use @Around proceed(newArgs)
  5. Mutating a referenced object still leaks through

basics

~10 s

Declare a JoinPoint parameter first. Use jp.getArgs() for arguments and jp.getSignature() for the method name/type. You can read them, but mutating the array does not change what the target actually receives.

solid answer

~40 s

In @Before advice, add a first parameter of type org.aspectj.lang.JoinPoint — Spring injects it automatically. jp.getArgs() returns the argument array, jp.getSignature() gives method/class info, jp.getTarget() is the underlying object, jp.getThis() is the proxy. For typed access you can bind specific args in the pointcut with args(), e.g. @Before("execution(* pay(..)) && args(amount)"). Crucially, @Before is read-only for arguments: reassigning entries in the getArgs() array does not alter the values the target method receives, because before advice does not control the invocation. If you genuinely need to transform arguments before the call, you must use @Around advice, which invokes proceed(Object[] newArgs) with modified arguments. So @Before is fine for inspecting/logging/validating args but not for rewriting them.

code

java · 25 lines
java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ArgInspectionAspect {

    // Typed binding via args(): clean and self-documenting
    @Before("execution(* com.app.PaymentService.charge(..)) && args(amount, currency)")
    public void validate(long amount, String currency) {
        if (amount <= 0) throw new IllegalArgumentException("amount must be > 0");
    }

    // Generic inspection via JoinPoint
    @Before("execution(* com.app.PaymentService.charge(..))")
    public void log(JoinPoint jp) {
        MethodSignature sig = (MethodSignature) jp.getSignature();
        System.out.printf("%s(%s)%n", sig.getName(),
                java.util.Arrays.toString(jp.getArgs()));
        // NOTE: jp.getArgs()[0] = ... here would be ignored by the target
    }
}

go deeper

for a junior

Know that a JoinPoint parameter gives access to getArgs() and getSignature().

for a middle

Explain args() binding and that @Before can't rewrite arguments — that needs @Around.

for a senior

Discuss the mutable-object leak subtlety and MethodSignature casting for reflection.

for a principal

Weigh clean args() binding vs generic JoinPoint inspection for API stability and testability across many aspects.

## Getting a JoinPoint Any advice method can declare a first parameter of type `org.aspectj.lang.JoinPoint`. Spring supplies it automatically — you don't pass it. For `@Before` this is the standard `JoinPoint` (not `ProceedingJoinPoint`, which only `@Around` gets, because only `@Around` can proceed). ```java @Before("execution(* com.app.PaymentService.charge(..))") public void audit(JoinPoint jp) { Signature sig = jp.getSignature(); // method + declaring type Object[] args = jp.getArgs(); // the arguments Object target = jp.getTarget(); // real bean instance Object proxy = jp.getThis(); // the AOP proxy } ``` ### Key JoinPoint methods - `getArgs()` → `Object[]` of arguments passed to the method. - `getSignature()` → a `Signature`; often cast to `MethodSignature` (`org.aspectj.lang.reflect.MethodSignature`) to get `getMethod()`, `getName()`, `getParameterNames()`, `getReturnType()`. - `getTarget()` → the unproxied target object. - `getThis()` → the proxy object. - `toShortString()` / `toLongString()` → readable descriptions for logging. ## Binding arguments by name (typed access) Instead of index-fishing in `getArgs()`, you can bind parameters directly using the `args()` pointcut designator. The names in `args(...)` match the advice method's parameter names: ```java @Before("execution(* com.app.PaymentService.charge(..)) && args(amount, currency)") public void checkAmount(long amount, String currency) { if (amount <= 0) throw new IllegalArgumentException("amount must be positive"); } ``` This is type-safe and self-documenting, and it also *narrows* the match to methods whose argument types are assignable to the bound parameter types. ## Can you modify arguments? No (in @Before) This is the central gotcha. `getArgs()` returns an array, and you *can* technically reassign its elements — but it has **no effect** on the arguments the target method actually receives. Before advice does not sit in the invocation path controlling the call; after it returns, Spring invokes the target with the **original** arguments. So: ```java @Before("execution(* charge(..)) && args(amount)") public void tryToMutate(JoinPoint jp) { jp.getArgs()[0] = 0L; // IGNORED — target still gets the original amount } ``` If you need to actually change arguments before the call, use `@Around`: ```java @Around("execution(* charge(..))") public Object rewrite(ProceedingJoinPoint pjp) throws Throwable { Object[] newArgs = pjp.getArgs(); newArgs[0] = sanitize(newArgs[0]); return pjp.proceed(newArgs); // proceed WITH modified args } ``` ## Mutable object arguments — a subtlety While you can't *replace* an argument in `@Before`, if an argument is a **mutable object**, calling setters on it in `@Before` *does* affect what the target sees (you're mutating the same referenced object, not swapping the reference). This is generally considered a bad practice (surprising side effects) but is technically possible and sometimes trips people up. ## Summary - Use `JoinPoint` for inspection: log, validate, audit. - Bind args by name with `args()` for clean typed access. - You cannot *rebind* arguments in `@Before`; reach for `@Around` + `proceed(newArgs)`. - Mutating a referenced object's internal state does leak through, but avoid it.

  • Why doesn't reassigning jp.getArgs()[0] change what the target receives?
    Before advice doesn't control the invocation; after it returns, Spring calls the target with the original arguments. Only @Around's proceed(newArgs) can substitute arguments.
  • How would you get the actual java.lang.reflect.Method inside advice?
    Cast getSignature() to MethodSignature and call getMethod(), e.g. ((MethodSignature) jp.getSignature()).getMethod().

saying these in an interview costs you the question

  • Believing jp.getArgs()[i] = newValue substitutes the argument for the target
  • Expecting a ProceedingJoinPoint in @Before (only @Around gets it)
  • Thinking you must manually pass the JoinPoint when calling

context