从零构建JDK动态代理核心机制
JDK动态代理的核心原理在于运行时动态生成代理类的字节码并将其加载到虚拟机中。通过自定义类加载器、反射机制以及动态源码生成与编译,我们可以手动还原这一过程。下面将详细拆解并实现一个极简版的JDK动态代理。
定义调用处理器接口
类似于原生的InvocationHandler,我们定义一个抽象接口,用于集中处理代理对象的方法调用,将实际执行逻辑委派给目标对象。
public interface CustomInvocationHandler {
Object invoke(Object proxyInstance, Method targetMethod, Object[] arguments) throws Throwable;
}
核心代理工厂实现
代理工厂负责完成代理对象的组装,主要包含五个关键步骤:
- 动态拼接代理类的Java源码字符串。
- 将源码写入磁盘生成
.java文件。 - 调用系统Java编译器将源码编译为
.class文件。 - 通过自定义类加载器将class文件加载进JVM。
- 利用反射实例化代理对象并传入调用处理器。
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import javax.tools.StandardJavaFileManager;
import javax.tools.JavaFileObject;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
public class ProxyFactory {
private static final String LINE_SEP = System.lineSeparator();
public static Object createProxyInstance(DynamicClassLoader loader, Class<?>[] interfaces, CustomInvocationHandler handler) {
try {
// 1. 生成代理类源码
String srcCode = buildSourceCode(interfaces[0]);
String basePath = loader.getBasePath();
File srcFile = new File(basePath, "$Proxy0.java");
// 2. 写入.java文件
Files.write(srcFile.toPath(), srcCode.getBytes(StandardCharsets.UTF_8));
// 3. 编译源码
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(srcFile);
compiler.getTask(null, fileManager, null, null, null, compilationUnits).call();
fileManager.close();
// 4. 加载class至JVM
Class<?> proxyClazz = loader.findClass("$Proxy0");
// 5. 实例化代理对象
Constructor<?> constructor = proxyClazz.getConstructor(CustomInvocationHandler.class);
srcFile.delete(); // 清理临时源文件
return constructor.newInstance(handler);
} catch (Exception e) {
throw new RuntimeException("代理实例生成失败", e);
}
}
private static String buildSourceCode(Class<?> intf) {
StringBuilder sb = new StringBuilder();
sb.append("import java.lang.reflect.Method;").append(LINE_SEP);
sb.append("public class $Proxy0 implements ").append(intf.getName()).append(" {").append(LINE_SEP);
sb.append(" private CustomInvocationHandler handler;").append(LINE_SEP);
sb.append(" public $Proxy0(CustomInvocationHandler handler) {").append(LINE_SEP);
sb.append(" this.handler = handler;").append(LINE_SEP);
sb.append(" }").append(LINE_SEP);
for (Method m : intf.getMethods()) {
sb.append(" @Override").append(LINE_SEP);
sb.append(" public ").append(m.getReturnType().getName()).append(" ").append(m.getName()).append("() {").append(LINE_SEP);
sb.append(" try {").append(LINE_SEP);
sb.append(" Method targetMethod = ").append(intf.getName()).append(".class.getMethod(\"").append(m.getName()).append("\");").append(LINE_SEP);
sb.append(" this.handler.invoke(this, targetMethod, null);").append(LINE_SEP);
sb.append(" } catch (Throwable t) { t.printStackTrace(); }").append(LINE_SEP);
sb.append(" }").append(LINE_SEP);
}
sb.append("}").append(LINE_SEP);
return sb.toString();
}
}
自定义类加载器
原生的类加载器无法直接加载我们动态生成的class字节码,因此需要重写findClass方法,将磁盘上的字节流转化为Class对象。
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class DynamicClassLoader extends ClassLoader {
private final String basePath;
public DynamicClassLoader(String basePath) {
this.basePath = basePath;
}
public String getBasePath() {
return this.basePath;
}
@Override
protected Class<?> findClass(String name) {
String classFilePath = basePath + File.separator + name + ".class";
File classFile = new File(classFilePath);
if (classFile.exists()) {
try {
byte[] classBytes = Files.readAllBytes(classFile.toPath());
// classFile.delete(); // 实际场景中应清理生成的.class文件
return defineClass(name, classBytes, 0, classBytes.length);
} catch (IOException e) {
throw new RuntimeException("类加载异常", e);
}
}
return null;
}
}
业务接口及实现
定义被代理的目标接口及其实现类。
public interface UserService {
String getUserName();
}
public class UserServiceImpl implements UserService {
@Override
public String getUserName() {
return "Admin_User";
}
}
测试与执行
通过传入目标对象的类加载信息、接口数组以及调用处理器实例,即可生成并执行代理逻辑。
public class ProxyTest {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
String basePath = System.getProperty("user.dir");
UserService proxy = (UserService) ProxyFactory.createProxyInstance(
new DynamicClassLoader(basePath),
target.getClass().getInterfaces(),
(proxyInstance, method, params) -> {
System.out.println("执行前增强逻辑...");
Object result = method.invoke(target, params);
System.out.println("执行后增强逻辑...");
return result;
}
);
proxy.getUserName();
}
}
动态生成的代理类结构
上述代码运行时,会在磁盘上动态生成类似以下结构的$Proxy0类,它实现了目标接口,并在每个方法中通过反射将调用委派给CustomInvocationHandler。
public class $Proxy0 implements UserService {
private CustomInvocationHandler handler;
public $Proxy0(CustomInvocationHandler handler) {
this.handler = handler;
}
@Override
public String getUserName() {
try {
Method targetMethod = UserService.class.getMethod("getUserName");
return (String) this.handler.invoke(this, targetMethod, null);
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
}