How does Spring decide between a JDK dynamic proxy and a CGLIB proxy, and how do you override that decision?
answer
- interface -> JDK, none -> CGLIB
- proxyTargetClass=true forces CGLIB
- Boot defaults to CGLIB
- CGLIB = runtime subclass
- final class/method not CGLIB-proxyable
basics
~10 sBy default Spring uses a JDK dynamic proxy if the target implements an interface, and CGLIB (a runtime subclass) if it doesn't. You force CGLIB everywhere with proxyTargetClass = true.
solid answer
~40 sSpring AOP's default policy: if the target bean implements at least one user interface, it creates a JDK dynamic proxy that implements those interfaces; if it implements none, it falls back to CGLIB, which subclasses the concrete class. You override this by setting proxyTargetClass = true — via @EnableAspectJAutoProxy(proxyTargetClass = true), <aop:config proxy-target-class="true">, or the property spring.aop.proxy-target-class=true — which forces CGLIB even when interfaces exist. Note that Spring Boot sets proxyTargetClass=true by default for its auto-proxying (so Boot apps often get CGLIB regardless). Choose JDK when you cleanly program to interfaces; choose/allow CGLIB when callers need the concrete type or the class has no interface. CGLIB caveats: it can't proxy final classes or final methods, and it invokes the target constructor.
code
java · 12 lines// Default: JDK proxy for interface beans, CGLIB for interface-less beans
@Configuration
@EnableAspectJAutoProxy
class DefaultAop { }
// Force CGLIB (subclass) proxies for ALL AOP beans
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
class CglibEverywhere { }
// Boot property equivalent (application.properties):
// spring.aop.proxy-target-class=false # restore JDK where interfaces existgo deeper
Know the interface-vs-none default and that a flag can force CGLIB.
State the full selection order and the three ways to set proxyTargetClass.
Add the Boot default and the CGLIB final/constructor caveats.
Reason about codebase-wide consistency and the risks of silent un-advised final methods.
## The two proxy technologies - **JDK dynamic proxy** — `java.lang.reflect.Proxy`, part of the JDK. **Interface-based**: the proxy implements the target's interfaces. Requires the target to implement an interface. - **CGLIB** (bundled inside spring-core) — **subclass-based**: the proxy is a runtime-generated *subclass* of the concrete target class, overriding methods to add advice. Works without any interface. ## Default selection algorithm (Spring AOP) 1. Is `proxyTargetClass` true? -> use **CGLIB**. 2. Else, does the target implement at least one non-internal interface? -> use **JDK dynamic proxy**. 3. Else -> use **CGLIB**. So out of the box: **interface present = JDK proxy; no interface = CGLIB**. ## Overriding the decision Force CGLIB (subclass) proxying: - Java config: `@EnableAspectJAutoProxy(proxyTargetClass = true)` - XML: `<aop:config proxy-target-class="true"/>` or `<aop:aspectj-autoproxy proxy-target-class="true"/>` - Property: `spring.aop.proxy-target-class=true` **Spring Boot note:** Boot's auto-configuration defaults `spring.aop.proxy-target-class` to **true**, so Boot applications typically use **CGLIB by default** even for beans that implement interfaces. Set it to `false` to restore JDK proxying where interfaces exist. ## Trade-offs / caveats **JDK dynamic proxy** - + No extra bytecode library semantics; only interface methods; clean 'program to interfaces'. - - Only methods declared on the interface are advised; concrete-type injection fails. **CGLIB** - + Works with no interface; proxy is assignable to the concrete class, so concrete-type injection works; can advise public methods not on any interface. - - Cannot subclass **final classes** or override **final/private/static** methods (those silently aren't advised). Historically invoked the constructor twice / required objenesis; modern Spring uses Objenesis so the constructor is bypassed for the proxy instance, but you should still not rely on constructor side effects in proxied beans. ## Rule of thumb If you already design around interfaces, JDK proxies are the natural fit. If a bean has no interface, or callers legitimately need the concrete type, CGLIB (or `proxyTargetClass=true`) is appropriate. Consistency across a codebase is often why teams just set `proxyTargetClass=true` everywhere.
- In a default Spring Boot app, which proxy type do you usually get for a service that implements an interface?CGLIB — Boot defaults spring.aop.proxy-target-class to true, so it subclass-proxies even interface-bearing beans unless you turn that off.
- Name a class shape CGLIB cannot proxy.A final class (can't be subclassed) — and it can't override final, private, or static methods, so advice on those is silently skipped.
saying these in an interview costs you the question
- Saying Spring always uses JDK proxies for Spring AOP.
- Claiming CGLIB needs an interface.
- Believing proxyTargetClass only affects performance, not proxy type.