BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ OOP and types

Java language

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

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.

What happens, in order
  1. 1You give the class loader, the interfaces to implement, and a handler: (proxy, method, args) -> { ...; return method.invoke(real, args); }
  2. 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.
  3. 3The proxy implements only interfacesFor classes without interfaces, libraries such as CGLIB or ByteBuddy generate a subclass instead (Spring does either).
Remember
Watch out
Expecting proxying to work for self-callsNot unwrapping InvocationTargetExceptionTrying to proxy a class that has no interface with the JDK proxy
Example (highlighted lines explain it)
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)); }    });  }}
  1. line 1 uses Proxy, the core of Dynamic proxies
  2. 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:

Part of a learning path

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.