What does proxyTargetClass=true do, and where can you set it?
answer
- proxy the class, not the interface
- forces CGLIB subclass
- on @Enable... annotations + Boot property
- final/private/static not advisable
- only changes behavior when interfaces exist
basics
~20 sproxyTargetClass=true tells Spring to always use CGLIB (a runtime subclass) instead of a JDK interface proxy, even when the bean implements interfaces. You set it on the AOP-enabling annotation, e.g. @EnableTransactionManagement(proxyTargetClass = true), or as a property.
solid answer
~40 sproxyTargetClass is a boolean that forces CGLIB class-based proxying. When true, Spring subclasses the target class at runtime rather than generating a JDK dynamic proxy over its interfaces — so the proxy is assignable to the concrete type, not just the interface. You can set it on the enabling annotations: @EnableAspectJAutoProxy(proxyTargetClass=true), @EnableTransactionManagement(proxyTargetClass=true), @EnableCaching(proxyTargetClass=true), and on ProxyFactory/ProxyFactoryBean directly. In Spring Boot the global property spring.aop.proxy-target-class controls it and defaults to true. Use it when you need to inject or reference beans by their concrete class, or when a class has no interface anyway (though there CGLIB is already forced). The cost: CGLIB can't proxy final classes or final/private methods, and it subclasses so a no-arg constructor was historically needed (Objenesis relaxes that).
code
java · 8 lines@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // force CGLIB for @Aspect advice
@EnableTransactionManagement(proxyTargetClass = true)
public class AopConfig { }
// Equivalent global switch in Spring Boot (application.properties):
// spring.aop.proxy-target-class=true (already the Boot default)
// spring.aop.proxy-target-class=false (opt back into JDK-when-interfaces)go deeper
Know it forces CGLIB and lives on @Enable... annotations.
List the three enabling annotations plus the Boot property and its true default.
Explain the injection-by-class motivation and CGLIB's final/private/Objenesis limitations.
Weigh a global CGLIB standard vs interface-only proxies as an architectural convention across services.
## The flag in one sentence `proxyTargetClass` is a configuration switch that says **'proxy the class, not the interface'** — i.e. use **CGLIB** class-based proxying instead of JDK interface-based proxying. ## Where you set it 1. **On AOP-enabling annotations** (the most common place): - `@EnableAspectJAutoProxy(proxyTargetClass = true)` — for `@Aspect` beans. - `@EnableTransactionManagement(proxyTargetClass = true)` — for `@Transactional`. - `@EnableCaching(proxyTargetClass = true)` — for `@Cacheable`/`@CacheEvict`. 2. **Programmatically** on `ProxyFactory`, `ProxyFactoryBean`, or `AbstractAutoProxyCreator#setProxyTargetClass(true)`. 3. **Globally in Spring Boot** via the property **`spring.aop.proxy-target-class`** (a single switch honored by `AopAutoConfiguration` and applied to the AnnotationAwareAspectJAutoProxyCreator). Boot's default is **true**. ## What changes when it's true - Spring always builds a **CGLIB subclass** of the target. The resulting proxy `instanceof TargetClass` is true, so `@Autowired MyServiceImpl` works. - When it's **false** (plain Spring default) and the bean implements interfaces, Spring builds a **JDK dynamic proxy** implementing only those interfaces — the proxy is NOT `instanceof MyServiceImpl`, only `instanceof MyServiceInterface`. ## Interaction with the 'no interface' case If a bean implements **no** interface, CGLIB is used regardless of the flag — there is simply no interface to proxy. So `proxyTargetClass=true` only *changes behavior* for beans that DO have interfaces. ## Why you'd turn it on - You (or a framework) inject/lookup beans **by concrete class**, which fails under a JDK proxy. - You want a single consistent proxy strategy app-wide (Boot's rationale for defaulting it true). - A class exposes public methods that are **not** on any interface but still need advice — a JDK proxy would only advise interface methods. ## Costs and limitations of forcing CGLIB - **Cannot subclass `final` classes**; **cannot override `final`, `private`, or `static` methods** — advice silently won't apply there. - CGLIB **subclasses** the target, so the class must be instantiable; historically a default (no-arg) constructor was required, but modern Spring uses **Objenesis** to bypass constructors. Constructor side-effects/field init may still surprise you because the constructor runs (or is bypassed) differently than you expect. - Slightly higher startup/memory cost from bytecode generation. ## Neutral facts to avoid confusion - The flag does **not** affect self-invocation semantics — internal `this.method()` calls bypass the proxy under either technology. - Setting it to false in Boot restores interface-based JDK proxying. ## When to use Leave Boot's default (true) unless you specifically want interface-only proxies (cleaner boundaries, easier mocking) — then set `spring.aop.proxy-target-class=false` and always inject by interface.
- If a bean has no interface, does proxyTargetClass=false give you a JDK proxy?No. With no interface there is nothing for a JDK proxy to implement, so Spring uses CGLIB regardless of the flag. The flag only matters for beans that implement interfaces.
saying these in an interview costs you the question
- Saying proxyTargetClass=true selects JDK proxies
- Thinking the flag can make a no-interface bean use a JDK proxy
- Believing it fixes self-invocation not being intercepted
- Assuming CGLIB can override final or private methods