Dynamic Proxies
Intercepting method calls at runtime — Proxy, InvocationHandler, a logging proxy, how Spring AOP and @Transactional work, and JDK proxies vs CGLIB.
#Introduction
In the previous chapter we learned how Reflection reads annotations. Now we'll learn one of the most powerful concepts in Java: the Dynamic Proxy.
Dynamic proxies are heavily used in Spring AOP, Spring transaction management, Spring Security, RMI, Mockito, logging frameworks and RPC frameworks. Many developers use these daily without realizing dynamic proxies are working behind the scenes.
#Why Do We Need a Dynamic Proxy?
interface PaymentService {
void pay();
}
class PaymentServiceImpl implements PaymentService {
public void pay() {
System.out.println("Payment Successful");
}
}Normally:
PaymentService service = new PaymentServiceImpl();
service.pay();Payment SuccessfulEverything works.
New Requirement
The manager says: print Logging Started before every payment and Logging Completed after it.
Logging Started
Payment Successful
Logging CompletedSolution 1 (Bad) — Modify the Original Class
class PaymentServiceImpl implements PaymentService {
public void pay() {
System.out.println("Logging Started");
System.out.println("Payment Successful");
System.out.println("Logging Completed");
}
}Problem: business logic and logging become tightly coupled.
Solution 2 (Also Bad) — Write a Wrapper by Hand
class PaymentServiceProxy implements PaymentService {
private PaymentService target;
PaymentServiceProxy(PaymentService target) {
this.target = target;
}
public void pay() {
System.out.println("Logging Started");
target.pay();
System.out.println("Logging Completed");
}
}It works. But imagine 100 interfaces → 100 proxy classes. Huge maintenance effort.
Solution 3 (Best) — Generate the Proxy Automatically
The proxy class is created at runtime, with no Java source code required. This is called a Dynamic Proxy.
#What is a Dynamic Proxy?
Real-Life Analogy
When you call customer care you don't reach the engineer directly — a receptionist receives the call, verifies your identity, records details and transfers the call. The receptionist acts as a proxy.
#Components of a Dynamic Proxy
Java provides two core types: java.lang.reflect.Proxy and java.lang.reflect.InvocationHandler.
InvocationHandler
The heart of a dynamic proxy. Every intercepted method passes through invoke():
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}Parameters of invoke()
| Parameter | Meaning |
|---|---|
proxy | The generated proxy object |
method | The method being called — for service.pay(), Reflection supplies the Method for pay() |
args | The arguments passed — for save("Rahul", 25), args contains "Rahul" and 25 (null when there are none) |
#Building a Logging Proxy
1. The Target Object
interface PaymentService {
void pay();
}
class PaymentServiceImpl implements PaymentService {
public void pay() {
System.out.println("Payment Successful");
}
}2. The InvocationHandler
class LoggingHandler implements InvocationHandler {
private Object target;
LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Logging Started");
Object result = method.invoke(target, args);
System.out.println("Logging Completed");
return result;
}
}Notice that Reflection is used here: method.invoke(target, args) executes the original method.
3. Create the Proxy Object
PaymentService service = new PaymentServiceImpl();
PaymentService proxy = (PaymentService) Proxy.newProxyInstance(
service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new LoggingHandler(service)
);4. Call the Proxy
proxy.pay();Logging Started
Payment Successful
Logging CompletedNotice: the business class never changed.
#Internal Flow
#Why is Reflection Required?
Intercepting Multiple Methods
interface EmployeeService {
void save();
void delete();
void update();
}One InvocationHandler can intercept all these methods. No separate proxy classes are required.
#Real-World Examples
Spring AOP — @Transactional
@Transactional
public void transfer() {
}The business method never contains transaction code.
Logging
For service.save(), the proxy performs: log start → method → execution time → log end — without modifying service code.
Security
For deleteEmployee(), the proxy first checks the user's role. If authorized it invokes the method; otherwise it throws an exception.
#Limitations of JDK Dynamic Proxies
A very important interview topic: JDK dynamic proxies work only with interfaces.
interface EmployeeService→ ✅ worksclass Employee {}(no interface) → ❌ fails
#JDK Dynamic Proxy vs CGLIB
| Feature | JDK Proxy | CGLIB |
|---|---|---|
| Requires an interface | Yes | No |
| Works on concrete classes | ✗ | ✓ (by subclassing) |
| Mechanism | java.lang.reflect.Proxy + Reflection | Bytecode generation (subclass), with reflection support |
| Limitations | Interface methods only | Can't proxy final classes or final/private methods |
| Used by Spring | ✓ | ✓ |
#How Spring Chooses a Proxy
This is classic Spring behaviour, and it can be configured.
#Advantages
- No code duplication
- Runtime proxy generation
- Logging
- Security
- Transactions
- Caching
- Performance monitoring
- Method interception
#Disadvantages
- Reflection overhead
- More difficult debugging
- JDK proxies require interfaces
- Complex call stack
#Interview Questions
Q1What is a dynamic proxy?
A runtime-generated object that intercepts method calls before forwarding them to the target object.
Q2Which class creates dynamic proxies?
java.lang.reflect.Proxy (via Proxy.newProxyInstance()).
Q3Which interface handles intercepted methods?
InvocationHandler.
Q4Which method is implemented?
invoke().
Q5Why is Reflection required?
Because the proxy doesn't know which method will be called until runtime. Reflection executes the correct method using method.invoke(target, args).
Q6Can a JDK dynamic proxy proxy normal classes?
No. Only interfaces.
Q7Which library proxies concrete classes?
CGLIB.
Q8How does Spring implement AOP?
Using dynamic proxies. Classic Spring uses a JDK dynamic proxy when an interface exists and CGLIB otherwise; Spring Boot defaults to CGLIB.
Q9Name real-world uses of dynamic proxies.
Spring AOP, transaction management, security, logging, caching, performance monitoring, remote procedure calls and Mockito.
#Key Points to Remember
- Dynamic proxies are generated at runtime and intercept method calls.
InvocationHandlercontains the interception logic.- Reflection (
method.invoke()) calls the original method. Proxy.newProxyInstance()creates proxy objects.- JDK dynamic proxies work only for interfaces; CGLIB works for concrete classes.
- Spring AOP is built on dynamic proxy technology.
#Chapter Summary
Dynamic proxies let Java intercept method calls without modifying the original business class. By combining Proxy, InvocationHandler and Reflection, Java frameworks implement cross-cutting concerns such as logging, transactions, security, caching and monitoring. Understanding dynamic proxies is essential for mastering Spring AOP and advanced Java framework internals.