Through the analysis of the previous articles, we know that the proxy class is generated through the ProxyClassFactory factory of the Proxy class. This factory class will call the generateProxyClass() method of the ProxyGenerator class to generate the bytecode of the proxy class. The ProxyGenerator class is stored in the sun.misc package. We can find this class through the OpenJDK source code. The core content of the generatedProxyClass() static method of this class is to call the generatedClassFile() instance method to generate Class files. Let's take a look at what is done inside the generateClassFile() method.
private byte[] generateClassFile() { //The first step is to assemble all methods into ProxyMethod objects//First generate proxy methods such as toString, hashCode, equals, etc. addProxyMethod(hashCodeMethod, Object.class); addProxyMethod(equalsMethod, Object.class); addProxyMethod(toStringMethod, Object.class); //Transfer each method of each interface and generate a ProxyMethod object for it 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 proxy methods with the same signature, check whether the return value of the method is compatible for (List<ProxyMethod> signatures : proxyMethods.values()) { checkReturnTypes(sigmethods); } //Step 2, assemble all field information and method information of the class file to be generated try { //Add the constructor method methods.add(generateConstructor()); //Transfer the proxy method in the cache for (List<ProxyMethod> signmethods : proxyMethods.values()) { for (ProxyMethod pm : signmethods) { //Add static fields of the proxy class, for example: private static Method m1; fields.add(new FieldInfo(pm.methodFieldName, "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC)); //Add proxy methods of the proxy class methods.add(pm.generateMethod()); } } //Add static field initialization method methods.add(generateStaticInitializer()); } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); } //The verification method and field collection cannot be greater than 65535 if (methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); } if (fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } if (fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } //Step 3, write to the final class file//Verify that there is the fully qualified name of the proxy class in the constant pool cp.getClass(dotToSlash(className)); //Verify that there is the fully qualified name of the proxy class parent class in the constant pool, and the parent class name is: "java/lang/reflect/Proxy" cp.getClass(superclassName); //Verify that the full qualified name of the proxy class interface for (int i = 0; i < interfaces.length; i++) { cp.getClass(dotToSlash(interfaces[i].getName())); } //Next to start writing the file, set the constant pool to read only cp.setReadOnly(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); try { //1. Write to the magic number dout.writeInt(0xCAFEBABE); //2. Write to the secondary version number dout.writeShort(CLASSFILE_MINOR_VERSION); //3. Write to the main version number dout.writeShort(CLASSFILE_MAJOR_VERSION); //4. Write to the constant pool cp.write(dout); //5. Write access modifier dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER); //6. Write class index dout.writeShort(cp.getClass(dotToSlash(className))); //7. Write parent class index, the generated proxy classes are inherited from Proxy dout.writeShort(cp.getClass(superclassName)); //8. Write interface count value dout.writeShort(interfaces.length); //9. Write interface set for (int i = 0; i < interfaces.length; i++) { dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName()))); } //10. Write field count value dout.writeShort(fields.size()); //11. Write field collection for (FieldInfo f : fields) { f.write(dout); } //12. Write method count value dout.writeShort(methods.size()); //13. Write method collection for (MethodInfo m : methods) { m.write(dout); } //14. Write property count value, the proxy class file has no attributes, so it is 0 dout.writeShort(0); } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); } //Convert to binary array to output return bout.toByteArray();}You can see that the generateClassFile() method is dynamically spliced according to the Class file structure. What is a Class file? Here we will first explain that the Java file we usually write ends with .java. After writing it, compile it through the compiler and generate a .class file. This .class file is a Class file. The execution of Java programs only depends on Class files and has nothing to do with Java files. This Class file describes the information of a class. When we need to use a class, the Java virtual machine will load the Class file of this class in advance and perform initialization and related verification. The Java virtual machine can ensure that these tasks will be completed before you use this class. We just need to use it with peace of mind, without caring about how the Java virtual machine loads it. Of course, Class files do not necessarily have to be compiled by compiling Java files. You can even write Class files directly through a text editor. Here, the JDK dynamic proxy dynamically generates Class files through programs. Let’s go back to the above code again and see that generating the Class file is mainly divided into three steps:
Step 1: Collect all the proxy methods to be generated, wrap them into ProxyMethod objects and register them into the Map collection.
Step 2: Collect all field information and method information to be generated for the Class file.
Step 3: After completing the above work, start assembling the Class file.
We know that the core part of a class is its fields and methods. Let's focus on the second step to see what fields and methods it generates for the proxy class. In the second step, the following four things were done in order.
1. Generate a parameter constructor for the proxy class, pass in a reference to the InvocationHandler instance and call the parameter constructor of the parent class.
2. Iterate over the Map collection of proxy methods, generate the corresponding Method type static domain for each proxy method, and add it to the fields collection.
3. Iterate over the Map collection of proxy methods, generate the corresponding MethodInfo object for each proxy method, and add it to the methods collection.
4. Generate a static initialization method for the proxy class. This static initialization method mainly assigns the reference of each proxy method to the corresponding static field.
Through the above analysis, we can roughly know that JDK dynamic proxy will eventually generate a proxy class with the following structure for us:
public class Proxy0 extends Proxy implements UserDao { //Step 1, generate constructor protected Proxy0(InvocationHandler h) { super(h); } //Step 2, generate static domain private static Method m1; //hashCode method private static Method m2; //equals method private static Method m3; //toString method private static Method m4; //... //Step 3, generate proxy method @Override public int hashCode() { try { return (int) h.invoke(this, m1, null); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public boolean equals(Object obj) { try { Object[] args = new Object[] {obj}; return (boolean) h.invoke(this, m2, args); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public String toString() { try { return (String) h.invoke(this, m3, null); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public void save(User user) { try { //Construct the parameter array, if there are multiple parameters added later, just Object[] args = new Object[] {user}; h.invoke(this, m4, args); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } //Step 4, generate the static initialization method static { try { Class c1 = Class.forName(Object.class.getName()); Class c2 = Class.forName(UserDao.class.getName()); m1 = c1.getMethod("hashCode", null); m2 = c1.getMethod("equals", new Class[]{Object.class}); m3 = c1.getMethod("toString", null); m4 = c2.getMethod("save", new Class[]{User.class}); //... } catch (Exception e) { e.printStackTrace(); } } }At this point, after layered analysis and in-depth exploration of the JDK source code, we restored the original appearance of the dynamically generated proxy class, and some of the previous questions were also well explained.
1. The proxy class inherits the Porxy class by default. Because Java only supports single inheritance, JDK dynamic proxy can only implement interfaces.
2. The proxy methods will call the invoke() method of InvocationHandler, so we need to rewrite the invoke() method of InvocationHandler.
3. When calling the invoke() method, the proxy instance itself, the target method and the target method parameters will be passed in. Explain how the parameters of the invoke() method come from.
Use the newly constructed Proxy0 as the proxy class to test again, and you can see that the final result is the same as the proxy class dynamically generated using JDK. Once again, our analysis is reliable and accurate. At this point, the JDK dynamic proxy series articles have been announced to end. Through the analysis of this series, the author has solved the long-standing doubts in his heart, and I believe that readers' understanding of JDK dynamic proxy has become one step further. However, the knowledge on paper is always shallow. If you want to better master JDK dynamic proxy technology, readers can refer to this series of articles to check the JDK source code by themselves, or exchange learning experience with the author, point out the author’s inappropriate analysis, learn together, and make progress together.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.