jdk动态代理源码底层(jdk生成字节码及5种字节码生产方式比较)
在前两篇文章中
java 的三种代理模式
jdk动态代理与cglib优势劣势以及jdk动态代理为什么要interface
(警惕动态代理导致的Metaspace内存泄漏问题,警惕动态代理导致的Metaspace内存泄漏问题)
讨论了jdk的动态代理
本文从源码级别了解一下,在源代码的基础上,加上
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
在注解的本质(yet)中,我们也曾经这么干,将代理类弄出来
Proxy0.class
public final class $Proxy0 extends Proxy implements IUserDao {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(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 void save() throws {【我们的方法】
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
h:
public class ProxyFactory implements InvocationHandler {
//维护一个目标对象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = method.invoke(target, args);
return returnValue;
}
//给目标对象生成代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}

return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);
Class<?> cl = getProxyClass0(loader, intfs);
return proxyClassCache.get(loader, interfaces);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
https://www.cnblogs.com/liuyun1995/p/8144706.html
通过前面几篇的分析,我们知道代理类是通过Proxy类的ProxyClassFactory工厂生成的,这个工厂类会去调用ProxyGenerator类的generateProxyClass()方法来生成代理类的字节码。ProxyGenerator这个类存放在sun.misc包下,我们可以通过OpenJDK源码来找到这个类,该类的generateProxyClass()静态方法的核心内容就是去调用generateClassFile()实例方法来生成Class文件。我们直接来看generateClassFile()这个方法内部做了些什么。
private byte[] generateClassFile() {
//第一步, 将所有的方法组装成ProxyMethod对象
//首先为代理类生成toString, hashCode, equals等代理方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象
for (int i = 0; i < interfaces.length; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
addProxyMethod(methods[j], interfaces[i]);
}
}
//对于具有相同签名的代理方法, 检验方法的返回值是否兼容
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
//第二步, 组装要生成的class文件的所有的字段信息和方法信息
try {
//添加构造器方法
methods.add(generateConstructor());
//遍历缓存中的代理方法
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
//添加代理类的静态字段, 例如:private static Method m1;
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
//添加代理类的代理方法
methods.add(pm.generateMethod());
}
}
//添加代理类的静态字段初始化方法
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
//验证方法和字段集合不能大于65535
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
//第三步, 写入最终的class文件
//验证常量池中存在代理类的全限定名
cp.getClass(dotToSlash(className));
//验证常量池中存在代理类父类的全限定名, 父类名为:"java/lang/reflect/Proxy"
cp.getClass(superclassName);
//验证常量池存在代理类接口的全限定名
for (int i = 0; i < interfaces.length; i++) {
cp.getClass(dotToSlash(interfaces[i].getName()));
}
//接下来要开始写入文件了,设置常量池只读
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
//1.写入魔数
dout.writeInt(0xCAFEBABE);
//2.写入次版本号
dout.writeShort(CLASSFILE_MINOR_VERSION);
//3.写入主版本号
dout.writeShort(CLASSFILE_MAJOR_VERSION);
//4.写入常量池
cp.write(dout);
//5.写入访问修饰符
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
//6.写入类索引
dout.writeShort(cp.getClass(dotToSlash(className)));
//7.写入父类索引, 生成的代理类都继承自Proxy
dout.writeShort(cp.getClass(superclassName));
//8.写入接口计数值
dout.writeShort(interfaces.length);
//9.写入接口集合
for (int i = 0; i < interfaces.length; i++) {
dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
}
//10.写入字段计数值
dout.writeShort(fields.size());
//11.写入字段集合
for (FieldInfo f : fields) {
f.write(dout);
}
//12.写入方法计数值
dout.writeShort(methods.size());
//13.写入方法集合
for (MethodInfo m : methods) {
m.write(dout);
}
//14.写入属性计数值, 代理类class文件没有属性所以为0
dout.writeShort(0);
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
//转换成二进制数组输出
return bout.toByteArray();
}
本质是用jdk的方式(不是javassist,不是cglib)构造了一个类(class文件字节码,非java文件)
private byte[] generateClassFile() {
//第一步, 将所有的方法组装成ProxyMethod对象 3 //首先为代理类生成toString, hashCode, equals等代理方法 4 addProxyMethod(hashCodeMethod, Object.class); 5 addProxyMethod(equalsMethod, Object.class); 6 addProxyMethod(toStringMethod, Object.class); 7 //遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象 8 for (int i = 0; i < interfaces.length; i++) { 9 Method[] methods = interfaces[i].getMethods(); 10 for (int j = 0; j < methods.length; j++) { 11 addProxyMethod(methods[j], interfaces[i]); 12 } 13 } 14 //对于具有相同签名的代理方法, 检验方法的返回值是否兼容 15 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 16 checkReturnTypes(sigmethods); 17 } 18 19 //第二步, 组装要生成的class文件的所有的字段信息和方法信息
//第二步, 组装要生成的class文件的所有的字段信息和方法信息
20 try {
21 //添加构造器方法
22 methods.add(generateConstructor());
23 //遍历缓存中的代理方法
24 for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
25 for (ProxyMethod pm : sigmethods) {
26 //添加代理类的静态字段, 例如:private static Method m1;
27 fields.add(new FieldInfo(pm.methodFieldName,
28 "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
29 //添加代理类的代理方法
30 methods.add(pm.generateMethod());
31 }
32 }
33 //添加代理类的静态字段初始化方法
//第三步, 写入最终的class文件
48 //验证常量池中存在代理类的全限定名
49 cp.getClass(dotToSlash(className));
50 //验证常量池中存在代理类父类的全限定名, 父类名为:"java/lang/reflect/Proxy"
51 cp.getClass(superclassName);
52 //验证常量池存在代理类接口的全限定名
//接下来要开始写入文件了,设置常量池只读
private ProxyGenerator.ConstantPool cp = new ProxyGenerator.ConstantPool(null);
57 cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
60 DataOutputStream dout = new DataOutputStream(bout);
61 try {。。。
return bout.toByteArray();
方法区 永久代 元空间 常量池文中所说,
常量池
1.6 - 永久代(方法区)
1.7、1.8 - 堆
看来有误,这个地方被塞入代理类的常量池应属于方法区(1.7永久带,1.8元空间)
其他参考:
https://www.jianshu.com/p/3137b33efc3f
动态代理方案,优缺点,比较(摘抄+自己的理解)
//测试结果 //创建代理的速度 Create JDK Proxy: 13 ms Create CGLIB Proxy: 217 ms //较慢 Create JAVAASSIST Proxy: 99 ms Create JAVAASSIST Bytecode Proxy: 168 ms //较慢 Create ASM Proxy: 3 ms //最快 ================ Run JDK Proxy: 2224 ms, 634,022 t/s //很慢,jdk生成的字节码考虑了太多的条件,所以字节码非常多,大多都是用不到的 Run CGLIB Proxy: 1123 ms, 1,255,623 t/s //较慢,也是因为字节码稍微多了点 Run JAVAASSIST Proxy: 3212 ms, 438,999 t/s //非常慢,完全不建议使用javassist的代理类来实现动态代理 Run JAVAASSIST Bytecode Proxy: 206 ms, 6,844,977 t/s //和asm差不多的执行速度,因为他们都是只生成简单纯粹的执行字节码,非常少(所以直接用javassist生成代理类的方式最值得推荐[从速度上讲]) Run ASM Bytecode Proxy: 209 ms, 6,746,724 t/s //asm什么都是最快的,毕竟是只和最底层打交道
浙公网安备 33010602011771号