Unravel โบ Java concepts โบ OOP and types
Dynamic proxies
Proxy.newProxyInstance builds an object at run time that implements an interface and sends every call to one InvocationHandler.
Learn these first
At a glance
A receptionist who answers every call for the boss: notes it down, decides whether to pass it on, and adds extra steps before and after.
- 1You give the class loader, the interfaces to implement, and a handler: (proxy, method, args) -> { ...; return method.invoke(real, args); }
- 2Every method call on the proxy arrives in invoke as a Method and an argument arrayThe handler may run code before, call the real object, run code after, or skip the call entirely.
- 3The proxy implements only interfacesFor classes without interfaces, libraries such as CGLIB or ByteBuddy generate a subclass instead (Spring does either).
- This is the mechanism behind many "magic" annotations (@Transactional, @Cacheable, Mockito mocks)
- Exceptions thrown by the real method arrive wrapped in InvocationTargetException; unwrap it in the handler or callers will see a confusing UndeclaredThrowableException
- The JDK generates a class at run time ($Proxy12) that implements your interfaces and forwards each method to handler.invoke
import java.lang.reflect.Proxy;public class Timing { @SuppressWarnings("unchecked") static <T> T timed(T real, Class<T> type) { return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type }, (proxy, method, args) -> { long t0 = System.nanoTime(); try { return method.invoke(real, args); } finally { System.out.println(method.getName() + " took " + (System.nanoTime() - t0)); } }); }}- line 1 uses Proxy, the core of Dynamic proxies
- line 5 uses Proxy, the core of Dynamic proxies
Read the full explanation (about 1 min)
Proxy.newProxyInstance builds an object at run time that implements an interface and sends every call to one InvocationHandler. It is how logging, security and transactions can wrap an object without editing it.
A mental model
A receptionist who answers every call for the boss: notes it down, decides whether to pass it on, and adds extra steps before and after.
How it works
You give the class loader, the interfaces to implement, and a handler: (proxy, method, args) -> { ...; return method.invoke(real, args); }.
Every method call on the proxy arrives in invoke as a Method and an argument array. The handler may run code before, call the real object, run code after, or skip the call entirely.
The proxy implements only interfaces. For classes without interfaces, libraries such as CGLIB or ByteBuddy generate a subclass instead (Spring does either).
Going deeper
This is the mechanism behind many "magic" annotations (@Transactional, @Cacheable, Mockito mocks). It also explains the usual pitfall: a call from one method to another inside the same object does not go through the proxy, so the extra behaviour is skipped.
Exceptions thrown by the real method arrive wrapped in InvocationTargetException; unwrap it in the handler or callers will see a confusing UndeclaredThrowableException.
Under the hood
The JDK generates a class at run time ($Proxy12) that implements your interfaces and forwards each method to handler.invoke. The class is defined through the class loader you passed in.
Common mistakes
- Expecting proxying to work for self-calls.
- Not unwrapping
InvocationTargetException. - Trying to proxy a class that has no interface with the JDK proxy.
How Unravel recognises it
When you paste code, Unravel looks for these signs of dynamic proxies. Spotting them yourself is a good habit when you read unfamiliar code:
- Types:
ProxyInvocationHandler - Method calls:
newProxyInstance()
Part of a learning path
- Object-oriented Java in depth ยท previous: Reflection
Related concepts
Frequently asked questions
What is Dynamic proxies?
Proxy.newProxyInstance builds an object at run time that implements an interface and sends every call to one InvocationHandler. It is how logging, security and transactions can wrap an object without editing it.
What are the most common mistakes with Dynamic proxies?
Expecting proxying to work for self-calls. Not unwrapping InvocationTargetException. Trying to proxy a class that has no interface with the JDK proxy.
How can I see Dynamic proxies in real code?
Paste your own Java into Unravel and it will point out where dynamic proxies appears, explain how it works at the detail level you choose, and draw the flow of the code around it.