我们知道,JDK 动态代理可以对接口进行代理,把接口的所有方法代理到 InvocationHandler 的 invoke 方法上。
示例如下:
package com.youthlin.example.proxy.service;
public interface IUserService {
String getName();
}
package com.youthlin.example.proxy;
import com.youthlin.example.proxy.service.IUserService;
import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 创建: youthlin.chen
* 时间: 2017-11-28 11:49
*/
public class Main {
public static void main(String[] args) throws IOException {
IUserService userService = (IUserService) Proxy.newProxyInstance(
IUserService.class.getClassLoader(),
new Class[]{IUserService.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return "YouthLin";
}
});
System.out.println("不同包接口 " + userService.getName());
//writeProxyClass(userService.getClass());
}
}
这样我们就可以对一个 没有实现 的接口进行调用了,实际的调用代理给了 IncovationHandler 实例的 invoke 方法。那么这个是怎么实现的呢?
我们可以使用 sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[]) 这个方法获取到生成的代理类的字节码数组,把字节码输出为一个 .class 文件,在 IDEA 中就可以看到源码。
private static void writeProxyClass(Class<?> proxyClass) throws IOException {
String proxyClassName = proxyClass.getName();
//System.writeProxyClass.println(proxyClassName);
proxyClassName = proxyClassName.substring(proxyClassName.lastIndexOf(".") + 1);
//System.writeProxyClass.println(proxyClassName);
byte[] proxyClassBytes = ProxyGenerator.generateProxyClass(proxyClassName, new Class[]{IHelloService.class});
String path = Main.class.getResource("/").getPath() + "/generate/";
File file = new File(path, proxyClass.getName().replaceAll("\\.", "/") + ".class");
if (!file.getParentFile().exists()) {
//noinspection ResultOfMethodCallIgnored
file.getParentFile().mkdirs();
}
System.out.println(proxyClass.getName() + " " + file.getAbsolutePath());
FileOutputStream out = new FileOutputStream(file);
out.write(proxyClassBytes);
out.flush();
out.close();
}
然后在 main 方法里调用 writeProxyClass(userService.getClass()); 就可以了。用 IDEA 打开输出文件,可以看到:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.youthlin.example.proxy.Main.IHelloService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy1 extends Proxy implements IHelloService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy1(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String sayHello(String var1) throws {
try {
return (String)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.youthlin.example.proxy.Main$IHelloService").getMethod("sayHello", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
也就是说,生成动态代理对象时,实际上是 JDK 动态编译了一个类,继承了 Proxy 类,实现了传入的接口类。然后把接口的方法(及 Object 的三个实例方法 toString, hashCode, equals)都代理给了父类 Proxy 的 InvocationHandler 实例。所以我们可以在自定义的 InvocationHandler 的 invoke 方法中进行处理。
Proxy
java.lang.reflect.Proxy#newProxyInstance
这个方法用于生成代理对象,里面比较重要的就是 Class<?> cl = getProxyClass0(loader, intfs); 这一句,拿到代理类的 Class 实例,之后 return cons.newInstance(new Object[]{h}); 即可生成代理对象。
java.lang.reflect.Proxy#getProxyClass0
这个方法实现很简单: return proxyClassCache.get(loader, interfaces); 从一个 proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); 缓存中获取类实例。其实真正的获取在这个地方:
java.lang.reflect.Proxy.ProxyClassFactory#apply
首先一些权限检查之类的,略过,拿到代理对象的类名,最终也落在 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); 这个调用上。最后通过本地方法将 byte[] 转化为 Class 对象就完成了。
ProxyGenerator
sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)
sun.misc.ProxyGenerator#generateClassFile
这里就是动态生成字节码的地方了。
其实是根据 JVM 规范,把类信息写入了字节数组
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
对应下面的 Class 规范:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}

本文代码可以在这里找到:
https://github.com/YouthLin/examples/tree/master/example-jdk-proxy
声明
- 本作品采用署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。除非特别注明, 霖博客文章均为原创。
- 转载请保留本文(《JDK 动态代理的实现》)链接地址: https://youthlin.com/?p=1647
- 订阅本站:https://youthlin.com/feed/
“JDK 动态代理的实现”上的1条回复
之前看过,忘的一干二净了。。