library
advancedProxy Pattern & Dynamic Proxies
Use java.lang.reflect.Proxy for runtime interface implementation and understand how frameworks use it.
A proxy stands in for another object and intercepts every call. Java can build one at runtime, with no class written in advance.
Dynamic proxy = a personal assistant who answers your phone. Callers (clients) think they're talking to you (real object). The assistant (proxy) can screen calls (security), take notes (logging), or forward them to you (delegation).
Key Concepts
1
Repo real = new JdbcRepo(ds);2
Repo proxy = (Repo) Proxy.newProxyInstance(
Repo.class.getClassLoader(),
new Class<?>[] { Repo.class },
(p, method, args) -> {
long t0 = System.nanoTime();
try {
return method.invoke(real, args); // delegate
} finally {
log.info("{} took {}ns", method.getName(), System.nanoTime() - t0);
}
});3
newProxyInstance generates a class implementing the interfaces you name. Every method on it is routed
to your InvocationHandler, which receives the Method and the arguments and decides what to do.
newProxyInstanceInvocationHandlerMethod
4
Which is how the frameworks you use work.
@Transactional a proxy opens a transaction, calls through,
then commits or rolls back
@Cacheable a proxy checks the cache before calling through
@Async a proxy submits the call to an executor
Hibernate a proxy loads the entity on first access
Mockito a proxy records calls instead of making them5
The limitation: interfaces only. Proxy cannot proxy a concrete class, so frameworks that need to fall
back on subclassing use bytecode generation:
Proxy
6
CGLIB generates a subclass, overriding each method
ByteBuddy the modern equivalent, used by Mockito and Hibernate7
Spring uses the JDK proxy when the bean has an interface, and CGLIB otherwise.
8
This explains the self-invocation gotcha.
@Transactional public void a() { b(); }
@Transactional public void b() { } // no new transaction9
The proxy wraps the object from outside. When a calls b through this, the call never leaves the object,
so the proxy never sees it and the annotation on b does nothing. People lose hours to this.
abthis
10
And a final method cannot be intercepted by a subclass proxy, which is why final on a Spring service
method can silently disable @Transactional.
final@Transactional
11
What the interviewer is probing.1. "Why does calling an @Transactional method from within the same class do nothing?" Probing:
self-invocation, which costs people hours. Stalls: "It should work." Moves up: the proxy wraps
the object from outside; an internal call through this never leaves the object, so the proxy never
sees it.
12
2. "What is the limitation of java.lang.reflect.Proxy?" Probing: interfaces only. Stalls:
"None." Moves up: it can only implement interfaces, which is why frameworks fall back on CGLIB or
ByteBuddy to subclass a concrete class.
13
3. "Why can a final method not be intercepted?" Probing: how subclass proxies work. Stalls:
"It can." Moves up: a subclass proxy overrides methods to intercept them, and a final method
cannot be overridden — which silently disables @Transactional on it.
14
4. "Name three frameworks built on dynamic proxies." Probing: breadth. Stalls: "Just
Spring." Moves up: Spring AOP, Hibernate lazy loading, Mockito mocks, and RPC client stubs.