JDK dynamic proxy ProxyGenerator generates the byte code file parsing of the proxy class, And JDK ProxyGenerator
Through the previous analysis, 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 byte code of the Proxy class. The ProxyGenerator class is stored in sun. under the misc package, we can find this Class through the OpenJDK source code. The core content of the generateProxyClass () static method of this Class is to call the generateClassFile () instance method to generate the Class file. Let's take a look at what is done inside the generateClassFile () method.
Private byte [] generateClassFile () {// The first step is to assemble all the methods into a ProxyMethod object // first generate the toString, hashCode, equals, and other proxy methods for the proxy class addProxyMethod (hashCodeMethod, object. class); addProxyMethod (jsonsmethod, Object. class); addProxyMethod (toStringMethod, Object. class); // traverses every method of each interface and generates the ProxyMethod object 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 with for (List <ProxyMethod> sigmethods: proxyMethods. values () {checkReturnTypes (sigmethods);} // step 2, assemble all the field information and method information of the class file to be generated. try {// Add the constructor method methods. add (generateConstructor (); // traverses the proxy method in the cache for (List <ProxyMethod> sigmethods: proxyMethods. values () {for (ProxyMethod pm: sigmethods) {// Add a static field 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 the proxy Method of the proxy class methods. add (pm. generateMethod () ;}// Add the static field Initialization Method of the proxy class methods. add (generateStaticInitializer ();} catch (IOException e) {throw new InternalError ("unexpected I/O Exception ");} // The verification method and field set 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");} // step 3, write the final class file // verify that the constant pool has the fully qualified cp of the proxy class. getClass (dotToSlash (className); // verify the full qualified name of the Proxy class parent class in the constant pool. The parent class name is "java/lang/reflect/Proxy" cp. getClass (superclassName); // verify that the constant pool has the full qualified name of the proxy interface for (int I = 0; I <interfaces. length; I ++) {cp. getClass (dotToSlash (interfaces [I]. getName ();} // The next step is to write data to the file. Set the constant pool read-only cp. setReadOnly (); ByteArrayOutputStream bout = new ByteArrayOutputStream (); DataOutputStream dout = new DataOutputStream (bout); try {// 1. write the magic number dout. writeInt (0 xCAFEBABE); // 2. write version number dout. writeShort (CLASSFILE_MINOR_VERSION); // 3. write to the main version dout. writeShort (CLASSFILE_MAJOR_VERSION); // 4. write a 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 the parent class index, and the generated Proxy classes are inherited from the Proxy dout. writeShort (cp. getClass (superclassName); // 8. 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 set for (FieldInfo f: fields) {f. write (dout);} // 12. write method Count value dout. writeShort (methods. size (); // 13. set of writing methods for (MethodInfo m: methods) {m. write (dout);} // 14. write attribute Count value. The proxy class file has no attribute, so it is 0 dout. writeShort (0);} catch (IOException e) {throw new InternalError ("unexpected I/O Exception");} // convert to a binary array and output return bout. toByteArray ();}
We can see that the generateClassFile () method is dynamically spliced according to the Class file structure. What is a Class file? Here, we need to explain that the Java file we usually write is. after the java program is compiled, it is generated by compiling through the compiler. class file. the class file is the Class file. The execution of Java programs only depends on the Class file and has nothing to do with the Java file. 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 validation, the Java Virtual Machine can complete these tasks before you use this class. We just need to use it with peace of mind without worrying about how the Java Virtual Machine loads it. Of course, the Class file does not have to be compiled by compiling the Java file. You can even compile the Class file directly through a text editor. Here, JDK dynamic proxy dynamically generates Class files through programs. Let's go back to the above Code again. We can see that there are three steps to generate a Class file:
Step 1: Collect all proxy methods to be generated, wrap them into ProxyMethod objects, and register them in the Map set.
Step 2: Collect all the field information and method information to be generated for the Class file.
Step 3: After completing the above work, start to assemble the Class file.
We know that the core part of a class is its fields and methods. We will focus on the second step to see which fields and methods it has generated for the proxy class. In step 2, we did the following four tasks in sequence.
1. Generate a constructor with parameters for the proxy class, pass in the reference of the InvocationHandler instance, and call the constructor with parameters of the parent class.
2. traverse the proxy Method Map set, generate a corresponding Method-Type Static domain for each proxy Method, and add it to the fields set.
3. traverse the proxy method Map set, generate the corresponding MethodInfo object for each proxy method, and add it to the methods set.
4. Generate a static Initialization Method for the proxy class. This static initialization method mainly assigns reference values of each proxy method to the corresponding static fields.
Through the above analysis, we can roughly know that JDK dynamic proxy will generate the following structure proxy class for us:
Public class Proxy0 extends Proxy implements UserDao {// Step 1: generate the constructor protected Proxy0 (InvocationHandler h) {super (h);} // step 2, generate the 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 the 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 multiple parameters are added to the backend, the Object [] args = new Object [] {user}; h. invoke (this, m4, args);} catch (Throwable e) {throw new UndeclaredThrowableException (e);} // Step 4, generate 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 ();}}}
So far, after layer-by-layer analysis and in-depth exploration of the JDK source code, we have restored the original face of the dynamically generated proxy class, and some questions in our hearts have also been 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 method calls the InvocationHandler's invoke () method. Therefore, we need to override the InvocationHandler's invoke () method.
3. When the invoke () method is called, the proxy instance itself, the target method, and the target method parameters are passed in. Explains how the parameters of the invoke () method come from.
Use the constructed Proxy0 as the proxy class for another test. You can see that the final result is the same as that of the dynamically generated proxy class using JDK. Once again, our analysis is reliable and accurate. So far, the JDK dynamic proxy series has ended. Through the analysis in this series, I have solved my long-standing doubts and believe that readers have a deeper understanding of JDK dynamic proxy. However, you may not be able to understand the JDK dynamic proxy technology on paper. To better master the JDK dynamic proxy technology, you can refer to the JDK source code in this series of articles and share your learning experience with the author, points out the author's improper analysis, learning together, and making progress together.
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.