Fundamentals 15 min read

Understanding Java Static and Dynamic Proxy Patterns

This article explains the concept of the Proxy design pattern, demonstrates how to implement static and dynamic proxies in Java with complete source code, and analyzes the advantages, limitations, and internal workings of Java's runtime-generated proxy classes.

Java Captain
Java Captain
Java Captain
Understanding Java Static and Dynamic Proxy Patterns

To understand Java dynamic proxy, you first need to know what a proxy is in the context of design patterns; the Proxy pattern is a structural pattern that wraps an object, providing the same method list while allowing additional behavior, and is closely related to the Decorator pattern.

Static Proxy

The following code shows a static proxy implementation where a SubjectImplProxy class implements the same Subject interface, holds a reference to a real SubjectImpl instance, and adds a prefix before delegating each method call.

package common;
public class Test {
    static interface Subject {
        void sayHi();
        void sayHello();
    }
    static class SubjectImpl implements Subject {
        @Override
        public void sayHi() { System.out.println("hi"); }
        @Override
        public void sayHello() { System.out.println("hello"); }
    }
    static class SubjectImplProxy implements Subject {
        private Subject target;
        public SubjectImplProxy(Subject target) { this.target = target; }
        @Override
        public void sayHi() { System.out.print("say:"); target.sayHi(); }
        @Override
        public void sayHello() { System.out.print("say:"); target.sayHello(); }
    }
    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        Subject subjectProxy = new SubjectImplProxy(subject);
        subjectProxy.sayHi();
        subjectProxy.sayHello();
    }
}

This static proxy simply wraps SubjectImpl to prepend "say:" to each output.

Dynamic Proxy

Static proxy has the drawback that every method needs identical wrapping code, and any new method added to the interface requires changes in the proxy class. The dynamic proxy solves this by generating a proxy class at runtime.

package common;
import java.lang.reflect.*;
public class Test {
    static interface Subject { void sayHi(); void sayHello(); }
    static class SubjectImpl implements Subject {
        @Override public void sayHi() { System.out.println("hi"); }
        @Override public void sayHello() { System.out.println("hello"); }
    }
    static class ProxyInvocationHandler implements InvocationHandler {
        private Subject target;
        public ProxyInvocationHandler(Subject target) { this.target = target; }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.print("say:");
            return method.invoke(target, args);
        }
    }
    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        Subject subjectProxy = (Subject) Proxy.newProxyInstance(
            subject.getClass().getClassLoader(),
            subject.getClass().getInterfaces(),
            new ProxyInvocationHandler(subject));
        subjectProxy.sayHi();
        subjectProxy.sayHello();
    }
}

The Proxy.newProxyInstance call performs three main steps: (1) generates bytecode for a class that implements all supplied interfaces and extends java.lang.reflect.Proxy ; (2) loads this class with the given class loader; (3) creates an instance using the constructor that accepts the custom InvocationHandler , allowing the proxy to be cast to the original interface.

The JDK source of Proxy.newProxyInstance validates arguments, obtains or generates the proxy class via getProxyClass , caches generated classes per class loader, and finally defines the class with defineClass0 . The caching mechanism ensures that the same set of interfaces reuses an existing proxy class, while synchronization prevents concurrent generation conflicts.

Generated proxy classes (e.g., $Proxy0 ) contain static Method fields for each interface method and delegate calls to the supplied InvocationHandler via reflection. The class also implements standard methods like equals , hashCode , and toString by forwarding them to the handler.

Summary

Dynamic proxies provide a unified way to wrap all methods of an object without modifying the proxy class when the target interface changes, but they require the target class to implement an interface because the generated proxy can only implement interfaces (Java does not support multiple inheritance of classes).

The dynamic proxy is called “dynamic” because the proxy class is generated and loaded at runtime, whereas static proxies are ordinary classes compiled ahead of time.

Javaproxyreflectiondesign patternDynamic ProxyStatic 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.