How does Spring decide between a JDK dynamic proxy and a CGLIB proxy when creating an AOP proxy for a bean?
answer
- interfaces -> JDK, none -> CGLIB
- CGLIB = runtime subclass
- proxyTargetClass=true forces CGLIB
- Boot default spring.aop.proxy-target-class=true
- DefaultAopProxyFactory decides
basics
~20 sIn plain Spring's default: if the bean implements at least one interface, Spring makes a JDK dynamic proxy; if it implements no interface, Spring uses CGLIB, which builds a subclass at runtime. Spring Boot changes the default to always use CGLIB.
solid answer
~40 sSpring wraps a bean in a proxy when it needs AOP (transactions, caching, custom aspects). The default rule in the core framework: if the target class implements at least one interface, Spring creates a JDK dynamic proxy that implements those interfaces; if it implements none, Spring falls back to CGLIB, which generates a runtime subclass of the target. Two overrides change this. Setting proxyTargetClass=true forces CGLIB even when interfaces exist. And Spring Boot's auto-configuration sets spring.aop.proxy-target-class=true by default, so a Boot app uses CGLIB everywhere unless you opt out. So the honest interview answer is: 'plain Spring picks JDK-when-interfaces / CGLIB-otherwise, but Spring Boot defaults to CGLIB for everything.'
code
java · 16 lines// Plain Spring: this bean implements an interface -> JDK dynamic proxy by default
public interface OrderService { void place(); }
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void place() { /* ... */ }
}
// Force CGLIB even though an interface exists:
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
class TxConfig { }
// In Spring Boot this is already the effective default because
// application.properties carries: spring.aop.proxy-target-class=truego deeper
Know the one-liner: interface -> JDK proxy, no interface -> CGLIB; Boot defaults to CGLIB.
Explain proxyTargetClass=true and the spring.aop.proxy-target-class Boot default, and how to flip it.
Connect proxy choice to injection-by-class vs injection-by-interface failures and mockability.
Reason about defaults across teams: why Boot standardized on CGLIB, and when to deliberately choose JDK proxies for boundary hygiene.
## What an AOP proxy is Spring implements cross-cutting concerns (declarative transactions via `@Transactional`, caching via `@Cacheable`, and any custom `@Aspect`) by wrapping your bean in a **proxy** object. Callers get the proxy instead of the raw bean; the proxy runs the advice (extra behavior) before/after delegating to your real object. Spring has two proxy technologies: - **JDK dynamic proxy** — built into the JDK (`java.lang.reflect.Proxy`). It can only proxy **interfaces**: it generates a class that implements the target's interface(s) and forwards calls through an `InvocationHandler`. The proxy is *not* a subclass of your concrete class — it only shares the interface type. - **CGLIB proxy** — Spring generates a **subclass** of your concrete class at runtime (CGLIB is repackaged inside `spring-core` as `org.springframework.cglib`) and overrides methods to insert advice. It needs a concrete, non-`final` class. ## The default selection rule (core Spring) Inside `DefaultAopProxyFactory` (used by `ProxyFactory`/`ProxyCreatorSupport`), the logic is roughly: if `proxyTargetClass` is true, OR the target has no interfaces, OR the target is already a proxy class, use CGLIB; **otherwise use a JDK dynamic proxy**. Practically: - Bean implements one or more interfaces -> **JDK dynamic proxy** (default). - Bean implements no interface -> **CGLIB** (no choice — JDK proxies can't proxy a bare class). ## Override 1 — `proxyTargetClass=true` Available on `@EnableAspectJAutoProxy(proxyTargetClass = true)`, `@EnableTransactionManagement(proxyTargetClass = true)`, `@EnableCaching(proxyTargetClass = true)`, and on `ProxyFactoryBean`/`ProxyFactory`. When true, Spring **forces CGLIB** even if interfaces exist — so the proxy is assignable to the concrete class type. ## Override 2 — Spring Boot's default Spring Boot's `AopAutoConfiguration` sets the property **`spring.aop.proxy-target-class=true` by default** (since Boot 2.0). So in a normal Boot application, CGLIB is used for everything, even beans that implement interfaces. To restore the JDK-when-interfaces behavior you must explicitly set `spring.aop.proxy-target-class=false`. ## Why the distinction matters - With a **JDK proxy**, the proxy is only type-compatible with the **interface**, not the class. Injecting the bean by its concrete class (`@Autowired MyServiceImpl x`) fails; you must inject the interface. - With **CGLIB**, the proxy IS-A subclass, so both interface and class injection work — one reason Boot prefers it. ## Gotchas - Neither proxy intercepts **self-invocation** (a method calling `this.other()` inside the same class bypasses the proxy) — that's orthogonal to proxy type. - CGLIB can't override `final` classes/methods or `private`/`static` methods, so advice silently doesn't apply there. ## When to use which Default to Boot's CGLIB unless you have a strong reason (e.g. a legacy `final` class or you specifically want interface-only proxies for clean boundaries and mockability).
- In a default Spring Boot app, a service implements an interface. Which proxy is used and why?CGLIB, because Boot's AopAutoConfiguration defaults spring.aop.proxy-target-class to true, which forces CGLIB regardless of interfaces.
- Why can't a JDK dynamic proxy be used for a class with no interfaces?JDK proxies are generated as classes implementing given interfaces via java.lang.reflect.Proxy; with no interface there is nothing to implement, so Spring must subclass the target with CGLIB.
saying these in an interview costs you the question
- Claiming Spring always uses CGLIB in plain (non-Boot) Spring
- Claiming JDK proxies subclass the target class
- Saying proxyTargetClass=true switches to JDK proxies
- Not knowing Spring Boot flips the default to CGLIB