动态代理最全详解系列[3]-ProxyGenerator生成代理类的字节码文件源码分析
在之前的生成代理类的源码分析中,我们会看到调用ProxyGenerator.generateProxyClass()生成代理类的字节码文件,下面我们到ProxyGenerator中看看,代理类的字节码到底是怎么生成的。
/**
*ProxyGenerator中的一些字段
*/
//代理类全限定名
private String className;
//代理类要实现的接口
private Class<?>[] interfaces;
//代理类访问标志
private int accessFlags;
//常量池
private ProxyGenerator.ConstantPool cp = new ProxyGenerator.ConstantPool();
//key:接口名,value:ProxyMethod列表,ProxyMethod由接口方法包装而成
private Map<String, List<ProxyGenerator.ProxyMethod>> proxyMethods = new HashMap();
/**
*ProxyGenerator.generateProxyClass()中会调用generateClassFile()生成代理类的字节码文件
*/
private byte[] generateClassFile() {
//第一步:将所有方法包装成ProxyMethod对象
//将hashCode、equals、toString方法包装成ProxyMethod对象
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
//获取代理类接口列表
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
//遍历每个接口的每个方法,并包装成ProxyMethod对象
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
//第二步:为代理类组装各种字段信息和方法信息
Iterator var15;
try {
//添加构造器方法,该构造器参数为InvocationHandler类型
this.methods.add(this.generateConstructor());
//添加静态字段及代理方法
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
//添加私有的静态字段,字段值为各方法的Method类
//10 = PRIVATE | STATIC
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
//添加代理方法
this.methods.add(var16.generateMethod());
}
}
//添加静态字段的初始化方法
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
//第三步:写入class文件
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
//验证常量池中存在代理类的全限定名
this.cp.getClass(dotToSlash(this.className));
//验证常量池中存在代理类父类的全限定名,即Proxy类
this.cp.getClass("java/lang/reflect/Proxy");
//验证常量池中存在代理类接口的全限定名
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
//写入class文件前,将常量池设为只读,当前常量池中的变量不允许修改
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
//每个class文件的前四个字节为魔数,用来确定这个文件是否是一个能被虚拟机接受的class文件,
//后面再跟两个字节的次版本号和两个字节的主版本号
//写入魔数
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);
}
}
}
通过分析代理类生成的流程,我们可以看到,generateClassFile()按照.class文件的结构拼接出了代理类的class文件。通常生成.class文件要先写一个.java文件,然后调用javac.exe命令或编译器自动将.java编译为.class,JVM将.class加载到内存后就可以运行了,而generateClassFile()跳过了.java直接拼出了.class。
我们再结合动态代理总结一下代理类class文件的组装过程:
1.收集代理类要实现的接口中的所有方法,包装成ProxyMethod对象,并通过Map保存接口名到ProxyMethod对象的映射;
2.添加参数类型为InvocationHandler的代理类构造器方法;
3.遍历Map,生成每个代理方法的Method类,作为静态字段添加到代理类中;
4.遍历Map,完成每个代理方法的实现,添加到代理类中;
5.添加静态初始化方法,将每个代理方法的引用赋值给对应的静态字段;
此时,我们再回头看一下上一篇中生成的$Proxy0代理类,就会看到所有的这些组装信息。
欢迎关注我的公众号【codeZhao】,获取更多技术干货和职业思考。

浙公网安备 33010602011771号