How Does Java’s JDK Dynamic Proxy Work Under the Hood? A Deep Dive
This article explains the difference between static and dynamic proxies in Java, walks through JDK dynamic‑proxy implementation steps, analyzes the underlying Proxy class generation process, and compares performance and usage with static proxies and CGLIB.
1. Static Proxy
Static proxy is a class created at compile time that implements the same interface as the target class and adds extra logic before or after delegating to the target.
/**
* User interface
*/
public interface IUserService {
/**
* Query all users
*/
void queryUserList();
}
/**
* Target class implementation
*/
public class UserServiceImpl implements IUserService {
@Override
public void queryUserList() {
System.out.println("Query all user information");
}
}
/**
* Proxy class implementation
*/
public class UserServiceProxy implements IUserService {
private UserServiceImpl userService;
public UserServiceProxy(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void queryUserList() {
System.out.println("----Authentication----");
System.out.println("Query all user information");
System.out.println("----Log recording----");
}
}
/**
* Test
*/
public class StaticAgentTest {
public static void main(String[] args) {
UserServiceProxy userServiceProxy = new UserServiceProxy(new UserServiceImpl());
userServiceProxy.queryUserList();
}
}Running the static proxy prints authentication, the original method output, and logging.
2. Dynamic Proxy
Dynamic proxy differs from static proxy in that the proxy class is generated at runtime. In Java there are two implementations: JDK dynamic proxy (interface‑based) and CGLIB dynamic proxy (class‑based).
2.1 JDK Dynamic Proxy
JDK dynamic proxy can only proxy classes that implement interfaces. It uses reflection to generate a proxy class that forwards method calls to an InvocationHandler.
2.1.1 Implementation Steps
Create an interface and its implementation.
Implement InvocationHandler to define custom logic.
Call Proxy.newProxyInstance to obtain the proxy instance.
/**
* User interface
*/
public interface IUserService {
void queryUserList();
}
/**
* Target class implementation
*/
public class UserServiceImpl implements IUserService {
@Override
public void queryUserList() {
System.out.println("Query all user information");
}
}
/**
* InvocationHandler implementation
*/
public class UserServiceInvocationHandler implements InvocationHandler {
private Object target;
public UserServiceInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----Proxy method before----");
Object result = method.invoke(target, args);
System.out.println("----Proxy method after----");
return result;
}
}
/**
* Test JDK dynamic proxy
*/
public class JdkProxyTest {
public static void main(String[] args) {
IUserService target = new UserServiceImpl();
IUserService proxyInstance = (IUserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new UserServiceInvocationHandler(target)
);
proxyInstance.queryUserList();
}
}Running the example prints “----Proxy method before----”, the original method output, and “----Proxy method after----”.
2.1.2 Underlying Mechanism and Proxy Source Analysis
Proxy.newProxyInstancecreates a proxy class that implements the target interfaces and delegates method calls to the supplied InvocationHandler. The method works by:
Validating the interfaces (visibility, uniqueness, and that they are true interfaces).
Choosing a package for the generated class – com.sun.proxy for public interfaces, otherwise the package of the non‑public interface.
Generating a unique class name like $Proxy0, $Proxy1 … using an AtomicLong counter.
Calling ProxyGenerator.generateProxyClass to produce the bytecode.
Defining the class with defineClass0 and constructing an instance via the constructor that takes an InvocationHandler.
The generated class extends java.lang.reflect.Proxy and contains static Method fields for each interface method, a constructor that stores the InvocationHandler, and overridden methods that invoke the handler’s invoke method.
public final class $Proxy0 extends Proxy implements IUserService {
private static Method m1; // Object.equals
private static Method m2; // Object.toString
private static Method m3; // IUserService.queryUserList
private static Method m4; // IUserService.delete
private static Method m0; // Object.hashCode
public $Proxy0(InvocationHandler h) throws Exception {
super(h);
}
public final void queryUserList() throws Throwable {
try {
super.h.invoke(this, m3, (Object[]) null);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
// other overridden methods (equals, hashCode, toString, delete) omitted for brevity
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.qy.staticAgent.IUserService").getMethod("queryUserList");
m4 = Class.forName("com.qy.staticAgent.IUserService").getMethod("delete", Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.getMessage());
}
}
}Saving the generated class file can be enabled by setting the system property sun.misc.ProxyGenerator.saveGeneratedFiles (JDK 8) or jdk.proxy.ProxyGenerator.saveGeneratedFiles (JDK 9+).
Summary
Static proxy is simple but requires a separate class for each target, leading to code duplication. JDK dynamic proxy creates proxy classes at runtime, works only for interfaces, and adds a small reflection overhead. For class‑based proxying, CGLIB can be used (previewed for the next article).
Recommended reading:
Jingdong second interview: Java serialization and deserialization
@ControllerAdvice Global Exception Handling Details
openFeign, where is my time?
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Xuanwu Backend Tech Stack
Primarily covers fundamental Java concepts, mainstream frameworks, deep dives into underlying principles, and JVM internals.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
