In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
JDK how to parse ProxyGenerator generation proxy class bytecode file, I believe many inexperienced people are helpless, this article summarizes the causes of the problem and solutions, through this article I hope you can solve this problem.
Through the analysis of the previous articles, we know that the proxy class is generated by the ProxyClassFactory factory of the Proxy class, which calls the generateProxyClass() method of the ProxyGenerator class to generate the bytecode of the proxy class. ProxyGenerator This class is stored under the sun.misc package, we can find this class through OpenJDK source code, the core content of the generateProxyClass() static method of this class is to call generateClassFile() instance method to generate Class file. Let's look directly at what generateClassFile() does internally.
1 private byte[] generateClassFile() { 2 //First step, assemble all methods into ProxyMethod object 3 //First generate proxy methods toString, hashCode, equals, etc. for proxy class4 addProxyMethod(hashCodeMethod, Object.class); 5 addProxyMethod(equalsMethod, Object.class); 6 addProxyMethod(toStringMethod, Object.class); 7 //Traverse each method of each interface and generate ProxyMethod object for it8 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 sigmethods : proxyMethods.values()) { 16 checkReturnTypes(sigmethods); 17 } 18 19 //第二步, 组装要生成的class文件的所有的字段信息和方法信息 20 try { 21 //添加构造器方法 22 methods.add(generateConstructor()); 23 //遍历缓存中的代理方法 24 for (List 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 //添加代理类的静态字段初始化方法 34 methods.add(generateStaticInitializer()); 35 } catch (IOException e) { 36 throw new InternalError("unexpected I/O Exception"); 37 } 38 39 //验证方法和字段集合不能大于65535 40 if (methods.size() >65535) { 41 throw new IllegalArgumentException("method limit exceeded"); 42 } 43 if (fields.size() > 65535) { 44 throw new IllegalArgumentException("field limit exceeded"); 45 } 46 47 //Step 3, write the final class file 48 //Verify that there is a fully qualified name 49 for the proxy class in the constant pool cp.getClass(dotToSlash(className)); 50 //Verify that there is a fully qualified name of the proxy class parent class in the constant pool, the parent class name is:"java/lang/reflect/Proxy" 51 cp.getClass(superclassName); 52 //Verify that the fully qualified name 53 of the proxy class interface exists in the constant pool for (int i = 0; i
< interfaces.length; i++) { 54 cp.getClass(dotToSlash(interfaces[i].getName())); 55 } 56 //接下来要开始写入文件了,设置常量池只读 57 cp.setReadOnly(); 58 59 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 60 DataOutputStream dout = new DataOutputStream(bout); 61 try { 62 //1.写入魔数 63 dout.writeInt(0xCAFEBABE); 64 //2.写入次版本号 65 dout.writeShort(CLASSFILE_MINOR_VERSION); 66 //3.写入主版本号 67 dout.writeShort(CLASSFILE_MAJOR_VERSION); 68 //4.写入常量池 69 cp.write(dout); 70 //5.写入访问修饰符 71 dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER); 72 //6.写入类索引 73 dout.writeShort(cp.getClass(dotToSlash(className))); 74 //7.写入父类索引, 生成的代理类都继承自Proxy 75 dout.writeShort(cp.getClass(superclassName)); 76 //8.写入接口计数值 77 dout.writeShort(interfaces.length); 78 //9.写入接口集合 79 for (int i = 0; i < interfaces.length; i++) { 80 dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName()))); 81 } 82 //10.写入字段计数值 83 dout.writeShort(fields.size()); 84 //11.写入字段集合 85 for (FieldInfo f : fields) { 86 f.write(dout); 87 } 88 //12.写入方法计数值 89 dout.writeShort(methods.size()); 90 //13.写入方法集合 91 for (MethodInfo m : methods) { 92 m.write(dout); 93 } 94 //14.写入属性计数值, 代理类class文件没有属性所以为0 95 dout.writeShort(0); 96 } catch (IOException e) { 97 throw new InternalError("unexpected I/O Exception"); 98 } 99 //转换成二进制数组输出100 return bout.toByteArray();101 } 可以看到generateClassFile()方法是按照Class文件结构进行动态拼接的。什么是Class文件呢?在这里我们先要说明下,我们平时编写的Java文件是以.java结尾的,在编写好了之后通过编译器进行编译会生成.class文件,这个.class文件就是Class文件。Java程序的执行只依赖于Class文件,和Java文件是没有关系的。这个Class文件描述了一个类的信息,当我们需要使用到一个类时,Java虚拟机就会提前去加载这个类的Class文件并进行初始化和相关的检验工作,Java虚拟机能够保证在你使用到这个类之前就会完成这些工作,我们只需要安心的去使用它就好了,而不必关心Java虚拟机是怎样加载它的。当然,Class文件并不一定非得通过编译Java文件而来,你甚至可以直接通过文本编辑器来编写Class文件。在这里,JDK动态代理就是通过程序来动态生成Class文件的。我们再次回到上面的代码中,可以看到,生成Class文件主要分为三步: 第一步:收集所有要生成的代理方法,将其包装成ProxyMethod对象并注册到Map集合中。 第二步:收集所有要为Class文件生成的字段信息和方法信息。 第三步:完成了上面的工作后,开始组装Class文件。 我们知道一个类的核心部分就是它的字段和方法。我们重点聚焦第二步,看看它为代理类生成了哪些字段和方法。在第二步中,按顺序做了下面四件事。 1.为代理类生成一个带参构造器,传入InvocationHandler实例的引用并调用父类的带参构造器。 2.遍历代理方法Map集合,为每个代理方法生成对应的Method类型静态域,并将其添加到fields集合中。 3.遍历代理方法Map集合,为每个代理方法生成对应的MethodInfo对象,并将其添加到methods集合中。 4.为代理类生成静态初始化方法,该静态初始化方法主要是将每个代理方法的引用赋值给对应的静态字段。 通过以上分析,我们可以大致知道JDK动态代理最终会为我们生成如下结构的代理类: 1 public class Proxy0 extends Proxy implements UserDao { 2 3 //第一步, 生成构造器 4 protected Proxy0(InvocationHandler h) { 5 super(h); 6 } 7 8 //第二步, 生成静态域 9 private static Method m1; //hashCode方法10 private static Method m2; //equals方法11 private static Method m3; //toString方法12 private static Method m4; //...13 14 //第三步, 生成代理方法15 @Override16 public int hashCode() {17 try {18 return (int) h.invoke(this, m1, null);19 } catch (Throwable e) {20 throw new UndeclaredThrowableException(e);21 }22 }23 24 @Override25 public boolean equals(Object obj) {26 try {27 Object[] args = new Object[] {obj};28 return (boolean) h.invoke(this, m2, args);29 } catch (Throwable e) {30 throw new UndeclaredThrowableException(e);31 }32 }33 34 @Override35 public String toString() {36 try {37 return (String) h.invoke(this, m3, null);38 } catch (Throwable e) {39 throw new UndeclaredThrowableException(e);40 }41 }42 43 @Override44 public void save(User user) {45 try {46 //构造参数数组, 如果有多个参数往后面添加就行了47 Object[] args = new Object[] {user};48 h.invoke(this, m4, args);49 } catch (Throwable e) {50 throw new UndeclaredThrowableException(e);51 }52 }53 54 //第四步, 生成静态初始化方法55 static {56 try {57 Class c1 = Class.forName(Object.class.getName());58 Class c2 = Class.forName(UserDao.class.getName()); 59 m1 = c1.getMethod("hashCode", null);60 m2 = c1.getMethod("equals", new Class[]{Object.class});61 m3 = c1.getMethod("toString", null);62 m4 = c2.getMethod("save", new Class[]{User.class});63 //...64 } catch (Exception e) {65 e.printStackTrace();66 }67 }68 69 } 至此,经过层层分析,深入探究JDK源码,我们还原了动态生成的代理类的本来面目,之前心中存在的一些疑问也随之得到了很好的解释 1.代理类默认继承Porxy类,因为Java中只支持单继承,所以JDK动态代理只能去实现接口。 2.代理方法都会去调用InvocationHandler的invoke()方法,因此我们需要重写InvocationHandler的invoke()方法。 3.调用invoke()方法时会传入代理实例本身,目标方法和目标方法参数。解释了invoke()方法的参数是怎样来的。Testing again using the proxy class just constructed, Proxy0, shows that the final result is the same as using a proxy class dynamically generated by JDK. Again, our analysis is reliable and accurate.
After reading the above, do you know how to parse the bytecode file of proxy class generated by ProxyGenerator in JDK? If you still want to learn more skills or want to know more related content, welcome to pay attention to the industry information channel, thank you for reading!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.