skip to content

How do this() and target() bindings relate to args(), and why can they behave differently under CGLIB vs JDK proxies?

level: seniorimportance: should knowfreq 28%

answer

  1. args=argument, target=real bean, this=proxy
  2. all bind by runtime type + by name
  3. JDK proxy: this(ConcreteClass) fails, interface works
  4. CGLIB proxy: this(ConcreteClass) matches (subclass)
  5. proxyTargetClass flip changes this() matching

basics

~20 s

Like args(), the this() and target() designators can bind objects into advice parameters by name. this() binds the proxy (the AOP object), target() binds the underlying target bean. They match on runtime type, and which types match can differ between JDK interface proxies and CGLIB class proxies.

solid answer

~40 s

args(), this(), and target() are the three binding PCDs. args(x) binds an argument; target(t) binds the underlying target bean instance; this(p) binds the AOP proxy that the caller holds. All match on runtime type and bind by name into advice parameters. The proxy subtlety: with JDK dynamic proxies the proxy implements the bean's interfaces but is not an instance of the concrete class, so this(ConcreteService) won't match while target(ConcreteService) will; with CGLIB the proxy is a subclass of the concrete class, so this(ConcreteService) matches. Practically, prefer target() when you want the real bean, and be aware that switching proxy strategy (proxyTargetClass) can change which this()-typed pointcuts fire.

code

java · 18 lines
java
@Aspect
@Component
public class BindingTrioAspect {

    // target() binds the real bean instance; args() binds the id.
    // Matches under both JDK and CGLIB proxies.
    @Before("target(repo) && args(id)")
    public void beforeFind(CrudRepository<?, ?> repo, Long id) {
        log.debug("{} looking up {}", repo.getClass().getSimpleName(), id);
    }

    // this() binds the PROXY. Under JDK proxies this must be an
    // interface type to match; AccountServiceImpl would not match.
    @Before("this(service)")
    public void beforeCall(AccountService service) {
        // `service` is the proxy the caller holds
    }
}

go deeper

for a junior

Awareness only: this()/target() can bind objects like args() binds arguments.

for a middle

Distinguish this()=proxy vs target()=real bean and that both match by runtime type.

for a senior

Explain JDK-vs-CGLIB effects on this() matching and prefer interface-typed or target() pointcuts for stability.

for a principal

Anticipate proxy-strategy-driven matching changes across a codebase, the self-invocation limitation, and design proxy-independent aspects.

**The binding trio.** Spring AOP has three PCDs that can *bind* a runtime object into an advice parameter, all matching by runtime type: - **args(name)** — binds a method *argument*. - **target(name)** — binds the *target object*: the actual bean instance that the proxy wraps (your real @Service class). - **this(name)** — binds the *proxy object*: the AOP proxy reference the caller is actually invoking through. Each can also be used purely as a type filter without binding, e.g. `target(com.app.Repository)` matches any join point whose target implements Repository. **Proxy background.** Spring creates a proxy around a bean to apply advice. Two mechanisms: 1. **JDK dynamic proxies** — used when the bean is proxied via its *interfaces*. The proxy implements those interfaces but is NOT a subclass of the concrete implementation class. 2. **CGLIB proxies** — used when there is no interface or when `proxyTargetClass=true`. The proxy is a runtime-generated *subclass* of the concrete class. **Why this() vs target() diverge.** - `target(x)` binds the underlying implementation instance, so `target(AccountServiceImpl)` matches under both proxy types — the target is always the concrete object. - `this(x)` binds the proxy. Under **JDK proxies**, the proxy is only an instance of the interfaces, so `this(AccountServiceImpl)` does NOT match (the proxy isn't that class), whereas `this(AccountService)` (the interface) does. Under **CGLIB**, the proxy is a subclass of AccountServiceImpl, so `this(AccountServiceImpl)` DOES match. Thus flipping proxy strategy (e.g. setting `spring.aop.proxy-target-class=true`, Spring Boot's default) can change whether a this()-typed pointcut fires. This is a classic "my aspect stopped/started matching after a config change" bug. **Relationship to args().** All three bind by name and rely on the same ParameterNameDiscoverer (-parameters/argNames). You commonly combine them: `@Before("target(repo) && args(id)")` gives you both the repository instance and the id argument. `this()` is useful when you need the proxy (e.g. to detect self-invocation issues or to access proxy-added interfaces from an introduction/@DeclareParents). **Self-invocation caveat.** Because this() binds the proxy, it highlights a core Spring AOP limitation: internal method calls (this.otherMethod()) bypass the proxy, so advice — and any this()/target() binding — won't fire for self-invocations. Understanding the proxy nature explains both the binding semantics and this limitation. **When to use.** Use target() to grab the real bean (most common). Use this() only when you specifically need the proxy or interfaces added by introductions. Keep pointcuts typed to *interfaces* when you want them proxy-strategy-independent. Prefer args() for argument values. **Gotchas.** (1) A bean with no interface is always CGLIB, so JDK-only assumptions break. (2) `final` classes/methods can't be CGLIB-proxied. (3) Mixing this()/target() with introductions is where CGLIB-vs-JDK matters most. (4) null/primitive matching follows the same runtime-type rules as args().

  • Why might this(MyServiceImpl) match after enabling proxyTargetClass but not before?
    Without it Spring may use a JDK interface proxy, which is not an instance of MyServiceImpl, so this() (the proxy) doesn't match. With CGLIB the proxy is a subclass of MyServiceImpl, so this() now matches.
  • If you want the actual bean instance regardless of proxy type, which do you use?
    target(). It always binds the underlying target object (the concrete bean), independent of JDK vs CGLIB proxying.

saying these in an interview costs you the question

  • Saying this() binds the target bean and target() binds the proxy (it's the reverse).
  • Assuming this(ConcreteClass) always matches regardless of proxy type.
  • Not knowing self-invocation bypasses the proxy and thus these bindings.

context