javaadvanced

Java Dynamic Proxy Pattern

Create dynamic proxies for cross-cutting concerns: logging, caching, transaction management, and metrics.

java
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.