Dynamic Proxy β Interview Questions
Reviewed by Gurusankar M. Β· Updated Aug 15, 2026
β‘ Short Answer
A dynamic proxy is a class generated at runtime that implements one or more interfaces and routes every method call to a single InvocationHandler.invoke(). That handler can add behaviour β logging, timing, transactions, security β before/after delegating to the real object. JDK proxies work on interfaces; CGLIB/ByteBuddy subclass concrete classes. It's the mechanism behind Spring AOP, @Transactional, and mock libraries.
βCoffee Chat Question
Concept Made Simple
βHow do Java dynamic proxies work, and how do frameworks use them for AOP?β
π§ Mind Map Answer
Remember It Faster
A proxy is a stand-in that looks like the real interface but funnels every call through your invoke() method, so you can wrap cross-cutting behaviour around the real target without editing it.
Spring wraps your bean in a proxy: calling save() actually hits the proxy, which opens a transaction, calls the real save(), then commits or rolls back β all without a line of transaction code in your method.
Key takeaway: proxies add behaviour around method calls at runtime. JDK = interfaces via InvocationHandler; CGLIB = subclassing β and self-invocation inside a bean bypasses the proxy.
Why CGLIB can't proxy a final class or method: CGLIB's whole mechanism is generating a runtime subclass of your target class that overrides each method to route through the interceptor. A final class can't be subclassed at all β there's nothing to generate. A final method on a non-final class can't be overridden either, so even a successfully generated subclass leaves that one method un-intercepted; a call to it goes straight to the real implementation and skips the proxy's advice entirely, silently.
If InvocationHandler.invoke() throws, the exception propagates to the caller exactly as if the real method had thrown it directly β the proxy transparently rethrows whatever invoke() throws (typically after Method.invoke() unwraps an InvocationTargetException back to its cause). The one sharp edge: if invoke() throws a checked exception that isn't declared in the proxied interface method's throws clause, the JVM wraps it in an unchecked UndeclaredThrowableException instead of letting it through as-is β a reminder that the proxy is bound by the interface's declared exception contract, not the real implementation's.
Multiple advices on one method compose as nested proxies, and order matters. A bean with both @Transactional and @Cacheable isn't wrapped by one proxy doing two things β Spring builds a chain, one proxy per advice, each wrapping the next like layers of an onion; the outermost proxy's invoke() runs first, calls into the next proxy, and so on until the innermost call finally reaches the real object. If @Cacheable sits outside @Transactional in that chain, a cache hit skips the transaction (and the real method) entirely and returns the cached value straight away β completely different behavior than the reverse ordering, where the transaction always opens even on a cache hit. Spring resolves this default ordering by advice type, but @Order (or Ordered) on the underlying Advisor lets you make it explicit rather than relying on the framework's default and being surprised later.
Proxies aren't free, but the overhead is smaller than it sounds. Every proxied call pays for one extra layer of indirection (dispatch through invoke()) plus, for JDK proxies specifically, a reflective Method.invoke() to reach the real implementation β reflection carries a real per-call cost relative to a direct method call, though the JIT compiler inlines and optimizes hot reflective call sites aggressively in long-running JVMs, so the gap narrows considerably after warm-up. In practice this overhead is negligible next to what the advice itself typically does (a database transaction, a cache lookup, a network call) β it becomes a real concern only in extremely hot, low-latency inner loops, which is exactly the kind of code most teams are careful not to route through Spring-managed beans in the first place.
β¨οΈHands-on Keyboard
Learn by Doing
interface Repo { void save(String s); }
Repo real = s -> System.out.println("saved " + s);
Repo proxy = (Repo) Proxy.newProxyInstance(
Repo.class.getClassLoader(),
new Class<?>[]{ Repo.class },
(p, method, args) -> { // InvocationHandler
System.out.println("BEGIN tx");
Object r = method.invoke(real, args); // delegate
System.out.println("COMMIT tx");
return r;
});
proxy.save("order-1");BEGIN tx saved order-1 COMMIT tx
π₯What If?
Think Beyond the Expected
Why does calling one @Transactional method from another method in the same bean skip the transaction?
Because the proxy only intercepts calls that come through it from outside. An internal `this.otherMethod()` call goes straight to the target object, bypassing the proxy wrapper entirely β so the advice (transaction, cache, retry) never runs. This 'self-invocation' gotcha is why Spring's proxy-based AOP can silently skip @Transactional/@Cacheable on internal calls; you fix it by refactoring into a separate bean, self-injecting the proxy, or switching to AspectJ weaving.
πReal World
Spring's @Transactional, @Cacheable, @Async, @Retryable, and security checks are all proxy-based advice; Mockito generates proxies for mocks; mapping and RPC libraries proxy interfaces to turn method calls into network requests. The self-invocation gotcha is one of the most common 'why didn't my transaction roll back?' bugs in real Spring apps.
π―Interviewer's Expectation
Keywords they're listening for:
β οΈCommon Mistakes
- βExpecting @Transactional to apply on self-invocation
- βTrying to JDK-proxy a class with no interface
- βForgetting CGLIB can't subclass final classes/methods
β Best Practices
- βProgram to interfaces so JDK proxies apply cleanly
- βAvoid self-invocation for proxied cross-cutting concerns
- βUse AspectJ weaving when proxy limits get in the way
πFollow-up Questions
- 1JDK dynamic proxy vs CGLIB β when is each used?
- 2How does reflection enable dynamic proxies?
- 3Why can't CGLIB proxy a final class or method?
- 4How does the JDK dynamic proxy relate to the classic Proxy design pattern (GoF)?
- 5What happens if InvocationHandler.invoke() throws an exception?
π§©Related Technologies
πReferences
Continue Learning with AI
Take this question deeper with your favourite AI assistant. Pick a depth, copy the prompt, or open it directly β AI is your learning companion, not a shortcut.
Plain-language foundations
I'm preparing for a software engineering interview and want to understand this from scratch, as a beginner. Topic: Dynamic Proxy (Advanced Java) Interview question: "How do Java dynamic proxies work, and how do frameworks use them for AOP?" Please: 1. Explain the core idea in simple, plain language, using an everyday analogy. 2. Define any technical terms you use. 3. Walk through one small, concrete example. 4. Finish with a single sentence I can easily remember. Keep the tone friendly and assume I'm new to this topic.
Was this answer helpful?
β Featured Products
Support our platform by exploring our recommended products.
As an Amazon affiliate, purchases through these links may earn us a small commission β at no extra cost to you. It helps keep Full Stack Interview Guru free.