Understanding JDK Dynamic Proxy: Implementation Principles and Code Walkthrough
This article explains how JDK dynamic proxy generates proxy classes at runtime for interfaces using java.lang.reflect.Proxy and InvocationHandler, demonstrates usage with code examples, explores the underlying ProxyGenerator and class‑caching mechanisms, and clarifies why only interface‑based proxies are supported.
JDK dynamic proxy creates proxy classes at runtime based on interfaces, using java.lang.reflect.Proxy and an InvocationHandler to intercept method calls.
Typical usage involves defining an interface (e.g., HelloWorld ), its implementation, a custom InvocationHandler that logs before and after invocation, and creating the proxy via Proxy.newProxyInstance .
The core of proxy generation resides in Proxy.getProxyClass0 , which builds a unique class name, generates bytecode via ProxyGenerator.generateProxyClass , caches classes with WeakCache , and defines the class with defineClass0 .
ProxyGenerator can optionally save the generated .class file when the system property sun.misc.ProxyGenerator.saveGeneratedFiles is set to true, allowing inspection of the generated bytecode.
Generated proxy classes extend java.lang.reflect.Proxy , implement the target interface, and override Object methods ( equals , hashCode , toString ) to delegate to the InvocationHandler , which explains why JDK proxies only support interface‑based proxies.
Understanding these mechanisms clarifies how Spring AOP leverages JDK dynamic proxies and provides a foundation for more advanced proxy or AOP implementations.
Example interface and implementation:
package com.mikan.proxy;
public interface HelloWorld {
void sayHello(String name);
}
public class HelloWorldImpl implements HelloWorld {
@Override
public void sayHello(String name) {
System.out.println("Hello " + name);
}
}Custom InvocationHandler:
package com.mikan.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class CustomInvocationHandler implements InvocationHandler {
private Object target;
public CustomInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invocation");
Object retVal = method.invoke(target, args);
System.out.println("After invocation");
return retVal;
}
}Proxy creation and execution:
package com.mikan.proxy;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) throws Exception {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
CustomInvocationHandler handler = new CustomInvocationHandler(new HelloWorldImpl());
HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(
ProxyTest.class.getClassLoader(),
new Class[]{HelloWorld.class},
handler);
proxy.sayHello("Mikan");
}
}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.
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.