Mechanically, how does a CGLIB proxy get created and instantiated, and what happens with the target's constructor?
answer
- Enhancer generates $$SpringCGLIB$$ subclass
- MethodInterceptor + invokeSuper
- Objenesis: instantiate without constructor
- ObjenesisCglibAopProxy
- proxy delegates, self-invocation bypasses
basics
~20 sSpring 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.
solid answer
~40 sAt startup Spring's ProxyFactory (through DefaultAopProxyFactory / ObjenesisCglibAopProxy) uses repackaged CGLIB's Enhancer to generate a subclass like YourType$$SpringCGLIB$$0. Each advisable method is overridden with a MethodInterceptor that runs the advice chain, then invokes the original body via the superclass. To create the proxy instance, older CGLIB called a target constructor, which could run constructor side effects twice; modern Spring uses Objenesis to allocate the instance without invoking any constructor, so the proxy's fields stay uninitialized and it delegates to the actual target bean rather than relying on its own constructor-set state. A consequence: don't rely on field initializers or constructor logic being re-run in the proxy — put real work in the target bean. The proxy is a distinct object from the target, which is why self-invocation (this.method()) bypasses advice.
code
java · 18 lines@Service
public class OrderService {
private final Auditor auditor; // constructor-injected into the REAL bean
public OrderService(Auditor auditor) { this.auditor = auditor; }
@Transactional
public void place(Order o) { auditor.record(o); }
}
// In a test you can verify the strategy Spring picked:
@Autowired OrderService orderService;
@Test
void usesCglib() {
assertThat(AopUtils.isCglibProxy(orderService)).isTrue();
// Class name looks like: OrderService$$SpringCGLIB$$0
// Objenesis created the proxy shell WITHOUT re-running the constructor.
}go deeper
Know a subclass is generated that overrides methods; details optional.
Understand Enhancer + MethodInterceptor + invokeSuper and the $$SpringCGLIB$$ name.
Explain Objenesis instantiation, the constructor-skip consequences, and proxy identity.
Reason about instantiation strategy history, field-init pitfalls, and testing/observability of proxy strategy.
**Where it happens.** During bean post-processing, an `AbstractAutoProxyCreator` (e.g. `AnnotationAwareAspectJAutoProxyCreator`) decides a bean needs advising and hands it to a `ProxyFactory`. `DefaultAopProxyFactory` picks the strategy: `JdkDynamicAopProxy` or `ObjenesisCglibAopProxy` (CGLIB). For CGLIB, Spring configures CGLIB's `Enhancer` with the superclass (your target type) and a set of `Callback`s. **Class generation.** The `Enhancer` emits a new class, named today like `YourType$$SpringCGLIB$$0`, that `extends YourType`. For every advisable (public/protected, non-final, non-static) method, the generated subclass overrides it. The override calls into a `MethodInterceptor` — Spring's `DynamicAdvisedInterceptor` — which walks the advisor/advice chain (`ReflectiveMethodInvocation`) and ultimately invokes the original method body via `MethodProxy.invokeSuper(...)`. Methods that need no advice can be routed through a fast pass-through callback, and CGLIB uses a `CallbackFilter` to map each method to the right callback. **Instantiation and the constructor problem.** A subtlety: to get an *instance* of the generated subclass, you must somehow construct it. Classic CGLIB instantiated via a constructor call, which meant the **target's constructor (and field initializers) ran again** as part of building the proxy — duplicating side effects and needing a matching constructor. Modern Spring avoids this with **Objenesis** (`ObjenesisCglibAopProxy`): Objenesis allocates the object *without running any constructor* (using JVM-specific instantiation tricks). So the proxy instance is created with uninitialized fields; it doesn't depend on its own constructor because it **delegates** advised calls to the underlying logic. This is why the class is `ObjenesisCglibAopProxy` and why Spring dropped the old requirement that CGLIB-proxied classes have a default constructor. **Implications of Objenesis instantiation:** - The proxy's own fields are *not* initialized by the target's constructor/field initializers. Any state or side effects your constructor performs are not re-executed in the proxy shell. Keep meaningful initialization in the real bean instance (which Spring does create normally) — for CGLIB, Spring targets the actual configured bean and the interceptor delegates to it. - Constructor-injected dependencies work fine because Spring still creates and wires the real target; the proxy just forwards. **Object identity & self-invocation.** The proxy is a *separate object* that IS-A subclass of the target. External callers hold the proxy. But a call from within the target to `this.otherMethod()` uses the target's own `this`, not the proxy, so it bypasses the interceptor and its advice. Workarounds: inject a self-reference to the proxy, use `AopContext.currentProxy()` (requires `exposeProxy = true`), or refactor into separate beans. **Naming / debugging.** Generated classes appear in stack traces as `YourType$$SpringCGLIB$$xxxx` (older Spring: `$$EnhancerBySpringCGLIB$$`). Seeing that name confirms a CGLIB proxy is in play. `AopUtils.isCglibProxy(bean)` / `AopUtils.isJdkDynamicProxy(bean)` let you assert the strategy in tests. **When it matters.** Understanding Objenesis-based instantiation explains odd behaviors: constructor breakpoints hit only once (for the real bean), `final` fields being fine, and why you shouldn't put business logic in constructors expecting the proxy to re-run them.
- Why did Spring adopt Objenesis for CGLIB proxies?To instantiate the generated subclass without invoking the target's constructor. This avoids running constructor side effects twice and removes the old requirement for a default/no-arg constructor on proxied classes.
- How can you tell at runtime whether a bean is a CGLIB or JDK proxy?Use AopUtils.isCglibProxy(bean) / AopUtils.isJdkDynamicProxy(bean), or inspect the class name (CGLIB proxies contain $$SpringCGLIB$$).
saying these in an interview costs you the question
- Saying the proxy re-runs the target's constructor to initialize its own fields (Objenesis skips it)
- Claiming CGLIB rewrites the original class rather than generating a subclass
- Believing the proxy and target are the same object (self-invocation depends on them being distinct)