Dynamic Proxies with InvocationHandler
Create JDK dynamic proxies to intercept interface method calls for logging, caching, and retry.
Dynamic Proxies with InvocationHandler is a free Java Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is a Dynamic Proxy?
A JDK dynamic proxy is an object created at runtime that implements one or more interfaces. All method calls are forwarded to an InvocationHandler that you provide.
Creating a Proxy with Proxy.newProxyInstance
Call Proxy.newProxyInstance(classLoader, interfaces[], handler) to create a proxy. The proxy implements all the listed interfaces.
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new LoggingHandler(realService)
);
proxy.findById(42L); // intercepted by LoggingHandlerThe InvocationHandler Interface
Implement InvocationHandler to handle every method call. The handler receives the proxy object, the invoked method, and the arguments.
public class LoggingHandler implements InvocationHandler {
private final Object target;
public LoggingHandler(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(">> " + method.getName());
Object result = method.invoke(target, args);
System.out.println("<< " + method.getName());
return result;
}
}Proxy for Caching
An InvocationHandler can cache results: if the cache contains a result for the arguments, return it; otherwise, call the real method and store the result.
public class CachingHandler implements InvocationHandler {
private final Object target;
private final Map<String, Object> cache = new HashMap<>();
public CachingHandler(Object target) { this.target = target; }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String key = method.getName() + Arrays.toString(args);
return cache.computeIfAbsent(key, k -> {
try { return method.invoke(target, args); }
catch (Exception e) { throw new RuntimeException(e); }
});
}
}Proxy for Retry Logic
Wrap method calls in a retry loop. If an exception occurs, retry up to N times before re-throwing.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
for (int attempt = 1; attempt <= 3; attempt++) {
try { return method.invoke(target, args); }
catch (InvocationTargetException e) {
if (attempt == 3) throw e.getCause();
Thread.sleep(1000L * attempt);
}
}
throw new RuntimeException("unreachable");
}Proxy for Transaction Management
Spring's @Transactional uses a dynamic proxy to begin a transaction before a method call and commit/rollback after — all without the caller knowing.
Checking If an Object Is a Proxy
Use Proxy.isProxyClass(obj.getClass()) to detect proxies, and Proxy.getInvocationHandler(proxy) to retrieve the handler.
if (Proxy.isProxyClass(service.getClass())) {
InvocationHandler h = Proxy.getInvocationHandler(service);
System.out.println("Handler: " + h.getClass().getSimpleName());
}Limitations of JDK Dynamic Proxies
JDK dynamic proxies only work with interfaces — they cannot proxy concrete classes. For class-based proxying, use CGLIB (which Spring also uses internally for @Configuration classes).
CGLIB as an Alternative
CGLIB generates a subclass of the target class at runtime, overriding all non-final methods. Spring uses it when no interfaces are present. No annotation required — it's transparent.
Proxy in Hibernate
Hibernate lazy-loads entity associations using CGLIB/Byte Buddy proxies. When you access a lazy collection, the proxy fires a SQL query.
// Hibernate creates a proxy for:
@ManyToOne(fetch = FetchType.LAZY)
private Department department;
// Accessing dept triggers a DB query via the proxyPerformance of Proxies
Dynamic proxy method dispatch is ~5–10× slower than a direct call. For very hot paths, consider compile-time code generation (APT) or MethodHandles instead of dynamic proxies.
Quick Check
What is the main limitation of JDK dynamic proxies?
Recap
JDK dynamic proxies intercept interface method calls via InvocationHandler. Use for logging, caching, retry, and transaction management. For class proxying, use CGLIB. Cache proxy creation — it's reflective and not free.
Frequently asked questions
Is the “Dynamic Proxies with InvocationHandler” lesson free?
Yes — the full text of “Dynamic Proxies with InvocationHandler” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.
What will I learn in “Dynamic Proxies with InvocationHandler”?
Create JDK dynamic proxies to intercept interface method calls for logging, caching, and retry. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Java Academy?
No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Dynamic Proxies with InvocationHandler” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Java Academy lesson?
Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Class Inspection with Reflection
- Invoking Methods and Accessing Fields
- Dynamic Proxies with InvocationHandler
- Reflection Performance and Alternatives