关于JDK动态代理的文章很多,基本都比较详细的讲解了动态代理的用法及基本原理,比如这篇文章:
Java JDK 动态代理(AOP)使用及实现原理分析
但是找了很多,都没有写代理类是怎么创建的,比如上面这篇文章,也只是说到调用ProxyGenerator.generateProxyClass()生成class文件字节数组。如果想知道底层是怎么生成的,还需要继续深入这块的代码。
idea直接打开ProxyGenerator类的代码,因为是反编译的,所以可读性很差,所以建议下载open jdk源码进行查看。
可以看到,核心的逻辑都在generateClassFile()方法中
并且这里还有个saveGeneratedFiles,用来判断是否要把class保存到文件中
启动参数添加sun.misc.ProxyGenerator.saveGeneratedFiles=true
会在当前工程目录下生成com/sun/proxy/$Proxy0.class文件
生成的class大致如下:
接着看generateClassFile的代码:
/**
* Generate a class file for the proxy class. This method drives the
* class file generation process.
*/
private byte[] generateClassFile() {
/* ============================================================
* Step 1: Assemble ProxyMethod objects for all methods to
* generate proxy dispatching code for.
*/
/*
* Record that proxy methods are needed for the hashCode, equals,
* and toString methods of java.lang.Object. This is done before
* the methods from the proxy interfaces so that the methods from
* java.lang.Object take precedence over duplicate methods in the
* proxy interfaces.
*/
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
/*
* Now record all of the methods from the proxy interfaces, giving
* earlier interfaces precedence over later ones with duplicate
* methods.
*/
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
}
/*
* For each set of proxy methods with the same signature,
* verify that the methods' return types are compatible.
*/
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
/* ============================================================
* Step 2: Assemble FieldInfo and MethodInfo structs for all of
* fields and methods in the class we are generating.
*/
try {
/*
* Write all the items of the "ClassFile" structure.
* See JVMS section 4.1.
*/
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout); // (write constant pool)
// u2 access_flags;
dout.writeShort(accessFlags);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (Class<?> intf : interfaces) {
dout.writeShort(cp.getClass(
dotToSlash(intf.getName())));
}
// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
return bout.toByteArray();
}
这里的代码看起来比较复杂,真要看懂需要了解jvm class构成规范。
这里其实就是先组装好要生成的class的信息,
然后再根据规范把数据写到DataOutputStream。
比如class文件开头是固定的CAFEBABE,
所以最开始就是:
dout.writeInt(0xCAFEBABE);
感兴趣的可以看下这篇文章:class文件魔数CAFEBABE的由来
生成class字节数组后,接着就是调defineClass0方法把class加载到jvm中了,如下图:
defineClass0是一个native方法,想要了解的话需要去看jdk源码。