skip to content

CGLIB Subclass Proxies

CGLIB proxies subclass the target, so no interface is needed, but a final class cannot be subclassed and final, private or static methods cannot be overridden. Those constraints are the interview point, especially with Kotlin's final-by-default classes.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

4

What is a CGLIB proxy in Spring, and how does it differ from a JDK dynamic proxy?

level: juniorimportance: must knowfreq 70%

answer

  1. runtime subclass overrides methods
  2. no interface needed
  3. org.springframework.cglib repackaged
  4. JDK proxy = interfaces, CGLIB = subclass
  5. Boot default proxyTargetClass=true

basics

~10 s

A CGLIB proxy is a runtime-generated subclass of your target class that overrides its methods to add behavior like transactions. Unlike a JDK dynamic proxy, it doesn't need the class to implement an interface.

solid answer

~40 s

Spring creates proxies to wrap beans with cross-cutting behavior (AOP advice, @Transactional, @Async, @Cacheable). There are two mechanisms. A JDK dynamic proxy implements the target's interfaces and requires at least one interface. A CGLIB proxy generates a runtime subclass of the concrete target class, overriding each non-final method to route calls through the interceptor chain. Because CGLIB subclasses, it works on classes with no interfaces. Spring ships a repackaged copy of the library under org.springframework.cglib so there's no external dependency. Spring Boot defaults to CGLIB (proxyTargetClass=true) even when interfaces exist, for consistency. The key limitation: since it subclasses, final classes can't be proxied and final, private, or static methods can't be overridden, so advice on those is silently skipped.

code

java · 12 lines
java
// No interface — Spring must use a CGLIB subclass proxy to advise @Transactional
@Service
public class PaymentService {

    @Transactional
    public void charge(long accountId, Money amount) {
        // proxy opens a tx before this body runs, commits/rolls back after
    }
}

// The bean Spring injects is actually a runtime-generated subclass:
//   class PaymentService$$SpringCGLIB$$0 extends PaymentService { ... }

go deeper

for a junior

Know CGLIB = runtime subclass, works without interfaces, adds behavior like @Transactional.

for a middle

Contrast JDK proxy (interface) vs CGLIB (subclass) and know Boot defaults to CGLIB.

for a senior

Explain the interceptor override mechanism and repackaged org.springframework.cglib.

for a principal

Discuss proxy-strategy tradeoffs, uniform CGLIB default rationale, and design implications for API surfaces.

**What a proxy is.** A proxy is a stand-in object that looks like your bean but wraps each method call with extra behavior (an "interceptor chain"). Spring uses proxies to implement AOP aspects and annotations such as `@Transactional`, `@Async`, `@Cacheable`, and `@Retryable`. When you call a method on the injected bean, you're actually calling the proxy, which runs the advice (e.g. open a transaction) and then delegates to your real code. **Two proxy strategies in Spring.** 1. **JDK dynamic proxy** — built into the JDK via `java.lang.reflect.Proxy`. It creates an object that *implements the same interfaces* as the target. It requires the bean to implement at least one interface, and only methods declared on those interfaces can be advised. The proxy holds a reference to the target and is a *separate* object from it. 2. **CGLIB proxy** — CGLIB (Code Generation Library) generates a brand-new class at runtime that *extends* (subclasses) your concrete target class. Spring bundles a repackaged copy of CGLIB under `org.springframework.cglib.*` (relocated from the original `net.sf.cglib`), so you don't add a dependency yourself. The generated subclass overrides every non-final, non-private method and inserts a `MethodInterceptor` call, which runs the advice chain and then invokes the original method body via the superclass. **Why CGLIB exists.** Not every bean implements an interface. Plenty of `@Service` / `@Component` classes are plain concrete classes. JDK proxies can't wrap those, so CGLIB subclassing fills the gap. **How Spring chooses.** Historically Spring used JDK proxies when the target implemented interfaces and CGLIB otherwise. You can force CGLIB with `proxyTargetClass = true` (on `@EnableAspectJAutoProxy`, `@EnableTransactionManagement`, etc.). **Spring Boot sets `proxyTargetClass = true` by default**, so Boot apps use CGLIB even for interface-bearing beans — this avoids surprises where injecting the concrete type fails. **Core limitations (because it subclasses):** - A `final` class cannot be subclassed → cannot be proxied by CGLIB. - `final` methods cannot be overridden → advice on them is silently skipped. - `private` methods aren't inherited/overridable → not advised. - `static` methods belong to the class, not instances → not proxied. **Gotcha — self-invocation.** Advice only fires when a call goes *through the proxy*. If a method calls another advised method on `this`, the call bypasses the proxy (it's a plain `super`-level call within the same object), so the second method's advice does not run. This is true for both proxy types. **When to use.** Prefer JDK proxies when you code to interfaces (cleaner, no subclassing constraints); use CGLIB when there's no interface, or use Boot's CGLIB default for uniform behavior. Avoid `final` on classes/methods you intend to advise.

  • If a class implements an interface, which proxy does Spring use?
    By default plain Spring uses a JDK dynamic proxy; but Spring Boot sets proxyTargetClass=true, so Boot uses CGLIB even then. You can also force CGLIB explicitly via proxyTargetClass.
  • Where does the CGLIB code come from — do you add a dependency?
    No. Spring repackages CGLIB inside spring-core under org.springframework.cglib, so it's always available without a separate artifact.

saying these in an interview costs you the question

  • Saying CGLIB requires the class to implement an interface (that's JDK proxies)
  • Claiming CGLIB modifies/rewrites the original class bytecode (it generates a new subclass)
  • Thinking you must add a cglib Maven dependency manually

context

open as a page

Why doesn't Spring AOP advice fire on final, private, or static methods when using CGLIB proxies?

level: middleimportance: must knowfreq 65%

basics

~10 s

CGLIB works by subclassing your class and overriding methods. Java doesn't let a subclass override final, private, or static methods, so those methods can't be intercepted and their advice is silently skipped.

open as a page

Mechanically, how does a CGLIB proxy get created and instantiated, and what happens with the target's constructor?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Spring generates a subclass of the target that overrides methods to call an interceptor chain, then delegates to the real code. Modern Spring instantiates the proxy via Objenesis without calling the target's constructor, avoiding side-effect duplication.

open as a page

How do you control whether Spring uses CGLIB vs JDK proxies, and what are the tradeoffs of Spring Boot forcing CGLIB by default?

level: principalimportance: should knowfreq 35%

basics

~10 s

Set proxyTargetClass=true to force CGLIB or false to prefer JDK proxies (on @EnableAspectJAutoProxy, @EnableTransactionManagement, etc., or via spring.aop.proxy-target-class). Boot defaults to CGLIB so injecting concrete types always works, at the cost of subclassing constraints.

open as a page