A powerful demonstration of Java Dynamic Proxies with Spring Boot! This project showcases how to intercept and modify object behavior at runtime without changing the original code.
- What is Dynamic Proxy?
- Project Structure
- Code Explanation
- How to Run
- Output Explanation
- Real-World Use Cases
Dynamic Proxy is a design pattern that intercepts method calls at runtime. It's especially useful when you need to:
- Add logging without modifying original code
- Implement security checks
- Monitor performance
- Add caching mechanisms
- Handle cross-cutting concerns
Client β Proxy Object β Original Object
β (Intercepts all method calls)
springBootProxies/
βββ app/
β βββ src/main/java/com/springbootproxies/
β β βββ App.java # Main Spring Boot application
β β βββ Person.java # Interface defining the contract
β β βββ Man.java # Concrete implementation
β β βββ PersonInvocationHandler.java # The proxy brain!
β βββ build.gradle # Dependencies and configuration
βββ README.md
public interface Person {
void introduce(String name);
void sayAge(String age);
void sayWhereFrom(String city, String country);
}
Purpose: This interface defines the contract that any Person implementation must follow. The proxy can only intercept methods defined in interfaces.
public class Man implements Person {
private String name;
private int age;
private String city;
private String country;
// Constructor and actual method implementations
@Override
public void introduce(String name) {
System.out.println("My name is " + this.name);
}
// ... other methods
}
Purpose: This is the actual implementation of the Person interface. It contains real data and provides the actual behavior that we want to proxy.
public class PersonInvocationHandler implements InvocationHandler {
private Person person;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("=== PROXY INTERCEPTED ===");
System.out.println("Method called: " + method.getName());
System.out.println("Before method execution");
// Call the actual method on the real object
Object result = method.invoke(person, args);
System.out.println("After method execution");
System.out.println("=== END PROXY ===");
return result;
}
}
Purpose:
- Intercepts every method call made to the proxy
- Adds logging before and after method execution
- Delegates the actual call to the real object
- Returns the result from the original method
// Create the original object
Man mohan = new Man("Mohan", 30, "Delhi", "India");
// Create the dynamic proxy
Person proxyMohan = (Person) Proxy.newProxyInstance(
mohan.getClass().getClassLoader(),
mohan.getClass().getInterfaces(),
new PersonInvocationHandler(mohan)
);
// Use the proxy - calls are automatically intercepted!
proxyMohan.introduce("test");
Key Components:
- ClassLoader: Loads the proxy class
- Interfaces: Array of interfaces the proxy should implement
- InvocationHandler: Handles all method calls
- Java 17+ installed
- Gradle installed (or use the Gradle wrapper)
-
Clone the repository:
git clone <your-repo-url> cd springBootProxies
-
Run the application:
./gradlew bootRun
Or alternatively:
./gradlew clean run
-
Stop the application:
Press Ctrl+C
When you run the application, you'll see this output:
=== DYNAMIC PROXY DEMO ===
--- PROXY CALLS ---
=== PROXY INTERCEPTED ===
Method called: introduce
Before method execution
My name is Mohan
After method execution
=== END PROXY ===
=== PROXY INTERCEPTED ===
Method called: sayAge
Before method execution
I am 30 years old
After method execution
=== END PROXY ===
--- DIRECT CALLS (NO PROXY) ---
My name is Mohan
I am 30 years old
I'm from Delhi, India
=== DEMO COMPLETED ===
- Proxy calls show logging around each method execution
- Direct calls only execute the original method
- This clearly demonstrates the proxy's interception behavior
Method method = person.getClass().getMethod("introduce", String.class);
method.invoke(person, "test");
Allows runtime access to method information and dynamic method invocation.
Proxy.newProxyInstance(classLoader, interfaces, invocationHandler)
Creates a dynamic proxy object that implements the specified interfaces.
The central point where all method calls are routed - provides a single place to handle cross-cutting concerns.
// Before method execution
logger.info("Calling method: " + method.getName());
Object result = method.invoke(target, args);
// After method execution
logger.info("Method completed successfully");
// Check if user has permission
if (!hasPermission(method)) {
throw new SecurityException("Access denied!");
}
return method.invoke(target, args);
long startTime = System.currentTimeMillis();
Object result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println("Execution time: " + (endTime - startTime) + "ms");
String cacheKey = method.getName() + Arrays.toString(args);
if (cache.containsKey(cacheKey)) {
return cache.get(cacheKey);
}
Object result = method.invoke(target, args);
cache.put(cacheKey, result);
return result;
try {
transactionManager.begin();
Object result = method.invoke(target, args);
transactionManager.commit();
return result;
} catch (Exception e) {
transactionManager.rollback();
throw e;
}
- Non-intrusive - No need to modify original code
- Flexible - Add behavior at runtime
- Reusable - Same proxy can be used for multiple objects
- Clean separation - Cross-cutting concerns handled separately
- Dynamic - Behavior can be changed at runtime
- Performance overhead - Additional method calls
- Debugging complexity - Stack traces can be harder to follow
- Interface requirement - Only works with interfaces (use CGLIB for classes)
- Runtime errors - Some issues only surface at runtime
- Java 17 - Core programming language
- Spring Boot 3.2.0 - Framework for easy application setup
- Gradle - Build automation tool
- Java Reflection API - For dynamic method invocation
- Proxy Pattern - Design pattern implementation
- Client calls method on proxy object
- Proxy intercepts the call and forwards to InvocationHandler
- InvocationHandler processes the call (adds logging, security, etc.)
- Original method is invoked on the real object
- Result is processed and returned to client
Client.method() β Proxy.method() β InvocationHandler.invoke() β RealObject.method()
- AOP Integration - Integrate with Spring AOP for more advanced features
- Custom Annotations - Method-level configuration using annotations
- Metrics Collection - Detailed performance and usage metrics
- Exception Handling - Advanced error management and recovery
- Configuration Management - External configuration for proxy behavior
The project demonstrates the proxy pattern with:
- Method interception - All calls are logged
- Transparent delegation - Original behavior is preserved
- Runtime behavior modification - No code changes needed
If you have improvements or suggestions:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
This project is open source and available under the MIT License.
Created with β€οΈ for learning Java Dynamic Proxies
"Understanding the Proxy pattern is essential for modern Java development!"