A través del análisis de los artículos anteriores, sabemos que la clase proxy se genera a través de la fábrica proxyClassFactory de la clase proxy. Esta clase de fábrica llamará al método GenerateProxyClass () de la clase ProxyGenerator para generar el código bytecodo de la clase proxy. La clase ProxyGenerator se almacena en el paquete Sun.Misc. Podemos encontrar esta clase a través del código fuente de OpenJDK. El contenido central del método estático GeneratedProxyClass () de esta clase es llamar al método de instancia GeneratedClassFile () para generar archivos de clase. Echemos un vistazo a lo que se hace dentro del método GenerateClassFile ().
byte privado [] generateClassFile () {// El primer paso es ensamblar todos los métodos en objetos proxymetodos // primero generar métodos proxy como toString, hashcode, iguales, etc. addProxyMethod (hashcodemethod, objeto.class); addProxyMethod (igualsmethod, object.class); addProxyMethod (toStringMethod, object.class); // transfiere cada método de cada interfaz y genere un objeto proxymethod para (int i = 0; i <interfaces.length; i ++) {método [] métodos = interfaces [i] .getMethods (); for (int j = 0; j <métodss.length; j ++) {addProxyMethod (métodos [j], interfaces [i]); }} // Para métodos proxy con la misma firma, verifique si el valor de retorno del método es compatible para (list <proxymethod> firmas: proxymethods.values ()) {checkreturntypes (sigmethods); } // Paso 2, ensamble toda la información de campo y la información del método del archivo de clase que se generará, pruebe {// agregue el método del constructor Methods.Add (generateConstructor ()); // Transfiere el método proxy en la caché para (List <ProxyMethod> Signmethods: proxyMethods.values ()) {for (proxymethod pm: signmethods) {// Agregar campos estáticos de la clase proxy, por ejemplo: método estático privado M1; Fields.Add (nuevo FieldInfo (pm.methodfieldName, "ljava/lang/reflej/método;", acc_private | acc_static)); // Agregar métodos proxy de los métodos de clase proxys.add (pm.generatemethod ()); }} // Agregar métodos del método de inicialización de campo estático. } Catch (ioException e) {tire nuevo internalErsor ("excepción de E/S inesperada"); } // El método de verificación y la colección de campo no pueden ser mayores que 65535 if (métodss.size ()> 65535) {tirar nueva ilegalArGumentException ("límite de método excedido"); } if (Fields.Size ()> 65535) {arrojar nuevo IlegalArgumentException ("Límite de campo excedido"); } if (Fields.Size ()> 65535) {arrojar nuevo IlegalArgumentException ("Límite de campo excedido"); } // Paso 3, escriba en el archivo de clase final // Verifique que haya el nombre totalmente calificado de la clase proxy en el grupo constante cp.getclass (dottostors (className)); // Verifique que haya el nombre completamente calificado de la clase principal de la clase proxy en el grupo constante, y el nombre de la clase principal es: "java/lang/reflej/proxy" cp.getclass (superclassname); // Verifique que el nombre calificado completo de la interfaz de la clase proxy para (int i = 0; i <interfaces.length; i ++) {cp.getClass (dotToshsh (interfaces [i] .getName ())); } // Siguiente para comenzar a escribir el archivo, configure el grupo constante para leer solo cp.setReadonly (); BytearRayOutputStream Bout = new ByteArRaReOutputStream (); DataOutputStream Dout = new DataOutputStream (BOUT); Prueba {// 1. Escriba al número mágico Dout.WriteInt (0xCafebabe); // 2. Escribir en el número de versión secundaria dout.writeshort (classfile_minor_version); // 3. Escribir en el número de versión principal dout.writeshort (classfile_major_version); // 4. Escribir en la piscina constante cp.write (dout); // 5. Modificador de acceso de escritura Dout.Writeshort (ACC_Public | ACC_Final | ACC_SUPER); // 6. Escribir class índice dout.writeshort (cp.getclass (dottostors (className))); // 7. Escribir el índice de clase principal, las clases proxy generadas se heredan de proxy dout.writeshort (cp.getclass (superclassname)); // 8. Escribir el valor de conteo de interfaz dout.writeshort (interfaces.length); // 9. Escriba el conjunto de interfaz para (int i = 0; i <interfaces.length; i ++) {dout.writeshort (cp.getClass (dottostore (interfaces [i] .getName ()))); } // 10. Escribir el valor de conteo de campo Dout.WriteShort (Fields.Size ()); // 11. Escribir colección de campo para (FieldInfo F: Fields) {F.Write (Dout); } // 12. Escribir el valor de conteo del método Dout.WriteShort (métodss.size ()); // 13. Colección de métodos de escritura para (MethodInfo M: Methods) {M.Write (Dout); } // 14. Escribir valor de conteo de propiedades, el archivo de clase proxy no tiene atributos, por lo que es 0 dout.writeshort (0); } Catch (ioException e) {tire nuevo internalErsor ("excepción de E/S inesperada"); } // Convertir a la matriz binaria a salida return Bout.tobytearray ();}Puede ver que el método GenerateClassFile () se splica dinámicamente de acuerdo con la estructura del archivo de clase. ¿Qué es un archivo de clase? Aquí explicaremos primero que el archivo Java que generalmente escribimos termina con .java. Después de escribirlo, compile a través del compilador y genere un archivo .class. Este archivo .class es un archivo de clase. La ejecución de los programas Java solo depende de los archivos de clase y no tiene nada que ver con los archivos Java. Este archivo de clase describe la información de una clase. Cuando necesitamos usar una clase, la máquina virtual Java cargará el archivo de clase de esta clase por adelantado y realizará la inicialización y la verificación relacionada. La máquina virtual Java puede garantizar que estas tareas se completen antes de usar esta clase. Solo necesitamos usarlo con tranquilidad, sin preocuparnos por cómo la máquina virtual Java lo carga. Por supuesto, los archivos de clase no necesariamente tienen que compilarse compilando archivos Java. Incluso puede escribir archivos de clase directamente a través de un editor de texto. Aquí, el proxy dinámico JDK genera dinámicamente archivos de clase a través de programas. Volvamos al código anterior nuevamente y veamos que generar el archivo de clase se divide principalmente en tres pasos:
Paso 1: recopile todos los métodos de proxy que se generarán, envuélvalos en objetos proxymethod y registérelos en la colección de mapas.
Paso 2: Recopile toda la información de campo y la información del método que se generará para el archivo de clase.
Paso 3: Después de completar el trabajo anterior, comience a ensamblar el archivo de clase.
Sabemos que la parte central de una clase son sus campos y métodos. Centrémonos en el segundo paso para ver qué campos y métodos genera para la clase proxy. En el segundo paso, las siguientes cuatro cosas se hicieron en orden.
1. Genere un constructor de parámetros para la clase proxy, pase en una referencia a la instancia de InvocationHandler y llame al constructor de parámetros de la clase principal.
2. Iterer sobre la colección de mapas de métodos proxy, generar el dominio estático de tipo de método correspondiente para cada método de proxy y agréguelo a la colección de campos.
3. Iterer sobre la colección de mapas de métodos proxy, generar el objeto MethodInfo correspondiente para cada método de proxy y agréguelo a la colección de métodos.
4. Genere un método de inicialización estática para la clase proxy. Este método de inicialización estática asigna principalmente la referencia de cada método proxy al campo estático correspondiente.
A través del análisis anterior, podemos saber aproximadamente que JDK Dynamic Proxy eventualmente generará una clase de proxy con la siguiente estructura para nosotros:
public class proxy0 extiende proxy implementos userdao {// Paso 1, generar constructor protegido proxy0 (InvocationHandler h) {super (h); } // Paso 2, generar el dominio estático Método estático privado M1; // método hashcode método estático privado m2; // iguales método Método estático privado M3; // Método de tostración Método estático privado M4; // ... // Paso 3, generar método proxy @Override public int hashcode () {try {return (int) h.invoke (this, m1, null); } catch (Throwable E) {Throw New Und -DeClaredThrowableException (e); }} @Override public boolean iguales (objeto obj) {try {object [] args = nuevo objeto [] {obj}; return (boolean) h.invoke (this, m2, args); } catch (Throwable E) {Throw New Und -DeClaredThrowableException (e); }} @Override public string toString () {try {return (string) H.invoke (this, m3, null); } catch (Throwable E) {Throw New Und -DeClaredThrowableException (e); }} @Override public void save (usuario de usuario) {try {// construye la matriz de parámetros, si hay múltiples parámetros agregados más tarde, solo objeto [] args = nuevo objeto [] {user}; H.invoke (this, m4, args); } catch (Throwable E) {Throw New Und -DeClaredThrowableException (e); }} // Paso 4, genere el método de inicialización estática estática {try {class c1 = class.forname (objeto.class.getName ()); Clase C2 = class.forname (userdao.class.getName ()); m1 = c1.getMethod ("hashcode", nulo); m2 = c1.getMethod ("iguales", nueva clase [] {object.class}); m3 = c1.getMethod ("toString", nulo); m4 = c2.getMethod ("guardar", nueva clase [] {user.class}); // ...} Catch (Exception e) {E.PrintStackTrace (); }}}En este punto, después del análisis en capas y la exploración en profundidad del código fuente JDK, restauramos la apariencia original de la clase proxy generada dinámicamente, y algunas de las preguntas anteriores también se explicaron bien.
1. La clase proxy hereda la clase Porxy por defecto. Debido a que Java solo admite la herencia única, el proxy dinámico JDK solo puede implementar interfaces.
2. Los métodos de poder llamarán al método Invoke () de InvocationHandler, por lo que necesitamos reescribir el método Invoke () de InvocationHandler.
3. Al llamar al método Invoke (), la instancia de proxy misma, el método de destino y los parámetros del método de destino se pasarán. Explique cómo provienen los parámetros del método Invoke ().
Use el proxy0 recientemente construido como la clase proxy para probar nuevamente, y puede ver que el resultado final es el mismo que la clase proxy generada dinámicamente usando JDK. Una vez más, nuestro análisis es confiable y preciso. En este punto, se ha anunciado que los artículos de la Serie Dynamic Proxy JDK se han anunciado para terminar. A través del análisis de esta serie, el autor ha resuelto las dudas de larga data en su corazón, y creo que la comprensión de los lectores del proxy dinámico JDK se ha convertido en un paso más allá. Sin embargo, el conocimiento sobre el papel siempre es superficial. Si desea mejorar la tecnología proxy dinámica de Master JDK, los lectores pueden consultar esta serie de artículos para verificar el código fuente de JDK por sí mismos, o intercambiar experiencia de aprendizaje con el autor, señalar el análisis inapropiado del autor, aprender juntos y avanzar juntos.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.