Java Interview Questions

Spring AOP: JDK Dynamic Proxy vs CGLIB

"How does Spring AOP work? When does it use JDK dynamic proxy vs CGLIB?"

Why Interviewers Ask This

AOP implementation is a classic Spring interview follow-up after IOC. It tests whether you understand the proxy pattern and bytecode-level differences — directly relevant to the "self-invocation bypasses @Transactional" production bug.

How to Answer

  1. 1

    Core idea: AOP creates a proxy around your bean that intercepts method calls to run advice (before, after, around) before delegating to the real method.

  2. 2

    JDK dynamic proxy: works only on interfaces — Proxy.newProxyInstance generates a class implementing the target interfaces at runtime, methods go through InvocationHandler.invoke.

  3. 3

    CGLIB: generates a subclass of the target at runtime by emitting bytecode. Works on classes that don't implement interfaces. Cannot proxy final classes or final methods.

  4. 4

    Spring's rule: if the target implements at least one interface, JDK proxy is used; otherwise CGLIB. Spring Boot 2.x+ defaults to CGLIB even when interfaces exist (spring.aop.proxy-target-class=true).

  5. 5

    Key gotcha: AOP only intercepts calls that go through the proxy. this.methodB() from within the same class hits the original object, bypassing advice — this is why @Transactional on an internal call silently fails.

Sample Answer

Spring AOP generates a proxy object that wraps your bean; external callers hold the proxy, and every method call first runs through advice chains (before, after, around) before delegating to the real method. Two proxy mechanisms. JDK dynamic proxy uses java.lang.reflect.Proxy to generate a class at runtime that implements all the target's interfaces. Method calls route through an InvocationHandler whose invoke() inspects the method and applies advice. Only works when the target implements interfaces. CGLIB uses bytecode manipulation (via ASM) to generate a subclass of the target at runtime. Methods are overridden; calls invoke the subclass, which applies advice before calling super. Works on plain classes but can't subclass final classes or override final/private methods. Spring's selection: default was "interface → JDK proxy, no interface → CGLIB". Spring Boot 2.x flipped the default to always-CGLIB (proxyTargetClass=true) because it avoids surprises when callers cast to the concrete type. The production-critical gotcha is self-invocation. If class Foo has two methods a() and b(), and b() is @Transactional, then this.b() from within a() calls the raw Foo instance method directly — not through the proxy — so the transaction advice never fires. Fixes: inject the bean into itself, or use AopContext.currentProxy() to get the proxy manually, or restructure so the @Transactional method is called from a different bean.

Pro Tips

  • Always tie AOP to the self-invocation gotcha — it's the #1 production bug involving Spring proxies.

  • Know that Spring Boot 2.x+ defaults to CGLIB — older knowledge about "JDK when possible" is outdated.

  • Mention final class / final method limitations — it signals bytecode understanding.

  • If you blank on proxyTargetClass during the interview, HireMe AI can surface the config flag.

Frequently Asked Questions

Why can't CGLIB proxy final classes?

CGLIB works by subclassing; a final class forbids subclassing, so CGLIB throws. Same for final methods — they can't be overridden.

What's the performance difference?

CGLIB is faster to invoke (direct method override) but slower to generate. JDK proxy is reflection-based. In steady state, both are negligible.

How do I verify which proxy is used?

Call bean.getClass().getName() — ends with $$EnhancerBySpringCGLIB for CGLIB, or $Proxy\d+ for JDK proxy.

Worried About Going Blank in Your Interview?

HireMe AI listens to your interview in real-time, detects questions, and generates instant answer suggestions — so you never freeze up again.

Try HireMe AI Free