Fundamentals 12 min read

Understanding the Proxy Pattern: Static vs Dynamic Proxy and AOP in Java

This article explains the proxy design pattern, compares static and dynamic proxy implementations in Java, demonstrates how to use Java reflection and InvocationHandler for dynamic proxies, and shows how these techniques enable AOP features such as logging and transaction handling.

Java Captain
Java Captain
Java Captain
Understanding the Proxy Pattern: Static vs Dynamic Proxy and AOP in Java

Proxy

Proxy pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it.

It allows the client to interact with the proxy as if it were the real object, enabling additional behavior such as logging, security, or lazy loading.

Static Proxy

Static proxy classes are written manually or generated by tools before compilation. They implement the same interface as the target class and delegate calls while adding extra logic.

public class UserManagerImpl implements UserManager {
    @Override
    public void addUser(String userId, String userName) {
        System.out.println("UserManagerImpl.addUser");
    }
    @Override
    public void delUser(String userId) {
        System.out.println("UserManagerImpl.delUser");
    }
    @Override
    public String findUser(String userId) {
        System.out.println("UserManagerImpl.findUser");
        return "张三";
    }
    @Override
    public void modifyUser(String userId, String userName) {
        System.out.println("UserManagerImpl.modifyUser");
    }
}

Proxy class that adds logging before and after each method call:

public class UserManagerImplProxy implements UserManager {
    private UserManager userManager;
    public UserManagerImplProxy(UserManager userManager) {
        this.userManager = userManager;
    }
    @Override
    public void addUser(String userId, String userName) {
        try {
            System.out.println("start-->addUser()");
            userManager.addUser(userId, userName);
            System.out.println("success-->addUser()");
        } catch (Exception e) {
            System.out.println("error-->addUser()");
        }
    }
    @Override
    public void delUser(String userId) { userManager.delUser(userId); }
    @Override
    public String findUser(String userId) { userManager.findUser(userId); return "张三"; }
    @Override
    public void modifyUser(String userId, String userName) { userManager.modifyUser(userId, userName); }
}

Client usage:

public class Client {
    public static void main(String[] args) {
        UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());
        userManager.addUser("1111", "张三");
    }
}

Dynamic Proxy

Dynamic proxies are created at runtime using reflection, allowing a single proxy class to handle multiple interfaces.

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
public class LogHandler implements InvocationHandler {
    private Object targetObject;
    public Object newProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("start-->>");
        for (Object arg : args) {
            System.out.println(arg);
        }
        Object ret = null;
        try {
            System.out.println("start-->>");
            ret = method.invoke(targetObject, args);
            System.out.println("success-->>");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("error-->>");
            throw e;
        }
        return ret;
    }
}

Client using dynamic proxy:

public class Client {
    public static void main(String[] args) {
        LogHandler logHandler = new LogHandler();
        UserManager userManager = (UserManager) logHandler.newProxyInstance(new UserManagerImpl());
        userManager.addUser("1111", "张三");
    }
}

Dynamic proxies centralize cross‑cutting concerns (logging, security, transactions) in the InvocationHandler, which is the core idea of Aspect‑Oriented Programming (AOP).

Summary

Both static and dynamic proxies achieve the same goal of controlling access to a target object, but dynamic proxies provide greater flexibility and reduce code duplication, making them suitable for implementing AOP concepts in Java applications.

JavaAOPreflectionDynamic ProxyProxy PatternStatic Proxy
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.