Java Dynamic Proxy Pattern
Create dynamic proxies for cross-cutting concerns: logging, caching, transaction management, and metrics.
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class DynamicProxyDemo {
// Generic proxy handler
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target, Class<T> iface, InvocationHandler handler) {
return (T) Proxy.newProxyInstance(
iface.getClassLoader(),
new Class<?>[]{iface},
handler
);
}
// Logging proxy
static <T> T withLogging(T target, Class<T> iface) {
return createProxy(target, iface, (proxy, method, args) -> {
System.out.printf("→ %s(%s)%n", method.getName(), Arrays.toString(args));
long start = System.nanoTime();
try {
Object result = method.invoke(target, args);
long ms = (System.nanoTime() - start) / 1_000_000;
System.out.printf("← %s returned %s (%dms)%n", method.getName(), result, ms);
return result;
} catch (InvocationTargetException e) {
System.out.printf("✗ %s threw %s%n", method.getName(), e.getCause());
throw e.getCause();
}
});
}
// Caching proxy
static <T> T withCaching(T target, Class<T> iface) {
Map<String, Object> cache = new ConcurrentHashMap<>();
return createProxy(target, iface, (proxy, method, args) -> {
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);
}
});
});
}
// Example usage
interface UserService {
String findById(int id);
List<String> findAll();
}
static class UserServiceImpl implements UserService {
public String findById(int id) {
try { Thread.sleep(100); } catch (InterruptedException e) {}
return "User-" + id;
}
public List<String> findAll() {
return List.of("Alice", "Bob", "Carol");
}
}
public static void main(String[] args) {
UserService service = new UserServiceImpl();
// Wrap with logging
UserService logged = withLogging(service, UserService.class);
logged.findById(42);
// Wrap with caching + logging
UserService cached = withCaching(
withLogging(service, UserService.class),
UserService.class
);
cached.findById(1); // hits DB
cached.findById(1); // hits cache
}
}Use Cases
- AOP-style cross-cutting concern handling
- Transparent caching layer
- Request logging and metrics collection
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
Builder Pattern — Fluent Object Construction
Implement the Builder pattern for complex objects with validation, immutability, and method chaining.
Best for: Constructing complex objects with many optional parameters
Singleton Pattern — Thread-Safe Approaches
Implement thread-safe singletons in Java: enum, holder class, double-checked locking, and eager init.
Best for: Application-wide configuration managers
Strategy Pattern with Lambdas
Implement the Strategy pattern using interfaces and Java lambdas for flexible algorithm selection.
Best for: Swappable pricing or discount algorithms
Factory Method Pattern with Registry
Implement the Factory pattern using a registry map for extensible object creation without switch statements.
Best for: Extensible object creation in plugin architectures