1. Definición de modo proxy
Proporcione un objeto con un objeto proxy, y el objeto proxy controla el acceso al objeto original, es decir, el cliente no manipula directamente el objeto original, sino que manipula indirectamente el objeto original a través del objeto proxy.
Un ejemplo de un patrón proxy famoso es el recuento de referencia: cuando se necesitan múltiples copias de un objeto complejo, el patrón de proxy se puede combinar con el modo meta para reducir la cantidad de memoria. Un enfoque típico es crear un objeto complejo y un proxy múltiple, cada proxy se refiere al objeto original. Las operaciones que actúan sobre el agente se enviarán al objeto original. Una vez que todos los agentes no existen, se eliminan objetos complejos.
Es simple entender el modelo proxy, pero de hecho hay un modelo proxy en la vida:
Podemos comprar boletos de trenes en la estación de tren, pero también podemos comprarlos en la oficina de ventas de boletos de tren. La oficina de ventas de boletos de trenes aquí es el agente de compras de boletos en la estación de tren. Es decir, emitimos una solicitud de compra de boletos en la salida de ventas. La salida de ventas enviará la solicitud a la estación de tren, y la estación de tren enviará la respuesta exitosa a la compra a la salida de ventas, y la salida de ventas le dirá nuevamente.
Sin embargo, los boletos solo se pueden comprar en la salida de ventas, pero no los reembolsos, mientras que los boletos se pueden comprar en la estación de tren, por lo que las operaciones respaldadas por el agente pueden ser diferentes de las del objeto comisionado.
Déjame darte otro ejemplo que encontrarás al escribir un programa:
Si hay un proyecto existente (no tiene código fuente, solo puede llamarlo) que puede llamar a Int Compute (String Exp1) para implementar el cálculo de la expresión de sufijo. Si desea utilizar este proyecto para implementar el cálculo de la expresión Infix, puede escribir una clase de proxy y definir un cómputo (String Exp2). Este parámetro exp2 es una expresión infijo. Por lo tanto, debe convertir la expresión INFIX en una expresión de sufijo (preprocesamiento) antes de llamar al compute () del proyecto existente, y luego llamar al compute () del proyecto existente. Por supuesto, también puede recibir el valor de retorno y hacer otras operaciones, como guardar el archivo (postprocesos). Este proceso utiliza el modo proxy.
Al usar una computadora, también encontrará aplicaciones de modo proxy:
Proxy remoto: no podemos acceder a Facebook debido a GFW en China. Podemos acceder a él navegando por la pared (configurando un proxy). El proceso de acceso es:
(1) El usuario envía la solicitud HTTP al proxy
(2) El proxy envía una solicitud HTTP al servidor web
(3) El servidor web envía la respuesta HTTP al proxy
(4) El proxy devuelve la respuesta HTTP al usuario
2. Proxy estático
El llamado proxy estático significa que se genera una clase proxy durante la etapa de compilación para completar una serie de operaciones en el objeto proxy. El siguiente es el diagrama de clase de estructura del patrón de poder:
1. Participantes en el modelo proxy
Hay cuatro roles en el modo proxy:
Interfaz del tema: es decir, la interfaz de comportamiento implementada por la clase proxy.
Objeto objetivo: es decir, el objeto está proxyed.
Objeto proxy: el cliente proxy utilizado para encapsular la clase de tema real es la siguiente estructura del diagrama de clases del patrón proxy:
2. Ideas para implementar el modelo de agente
Tanto el objeto proxy como el objeto objetivo implementan la misma interfaz de comportamiento.
La clase proxy y la clase de destino implementan la lógica de la interfaz por separado.
Instanciar un objeto objetivo en el constructor de la clase proxy.
Llamar a la interfaz de comportamiento del objeto objetivo en la clase proxy.
Si el cliente desea llamar a la interfaz de comportamiento del objeto de destino, solo puede funcionar a través de la clase proxy.
3. Ejemplos de poder estático
El siguiente es un ejemplo de carga perezoso para ilustrar el proxy estático. Cuando iniciamos un sistema de servicio, puede llevar mucho tiempo cargar una determinada clase. Para obtener un mejor rendimiento, al comenzar el sistema, a menudo no inicializamos esta clase compleja, sino que inicializamos su clase de proxy. Esto separará los métodos de consumo de recursos utilizando proxy para la separación, lo que puede acelerar la velocidad de inicio del sistema y reducir el tiempo de espera del usuario.
Definir una interfaz de tema
sujeto de interfaz pública {public void sayshello (); Public void Saysgoodbye ();} Definir una clase de destino e implementar la interfaz del tema
public class RealSubject implementa sujeto {public void sayshello () {System.out.println ("Hello World"); } public void SaysgoodBye () {System.out.println ("Adiós mundo"); }} Defina una clase de proxy para proxy del objeto objetivo.
clase pública staticProxy implementa sujeto {private RealSubject RealSubject = null; public staticProxy () {} public void sayshello () {// se carga en ese momento, la carga de lazada if (realSubject == null) {realSubject = new RealSubject (); } RealSubject.SayHello (); } // El método Saygoodbye es el mismo ...} Defina un cliente
Cliente de clase pública {public static void main (string [] args) {staticproxy sp = new staticProxy (); sp.sayhello (); sp.saygoodbye (); }}Lo anterior es un simple ejemplo de prueba de un proxy estático. Puede que no se sienta práctico. Sin embargo, este no es el caso. Usando un proxy, también podemos transformar los métodos del objeto objetivo. Por ejemplo, se crea una serie de conexiones en el grupo de conexión de la base de datos. Para asegurarse de que las conexiones se abran con poca frecuencia, estas conexiones casi nunca están cerradas. Sin embargo, siempre tenemos el hábito de cerrar la conexión abierta. De esta manera, podemos usar el modo proxy para reproducir el método Cerrar en la interfaz de conexión y cambiarlo para reciclarlo en el grupo de conexión de la base de datos en lugar de ejecutar realmente el método de cierre de conexión#. Hay muchos otros ejemplos, y necesitas experimentarlos tú mismo.
3. Agente dinámico
El proxy dinámico se refiere a la generación dinámica de las clases proxy en tiempo de ejecución. Es decir, el bytecode de la clase proxy se generará y se cargará en tiempo de ejecución al cargador de clases del proxy actual. En comparación con las clases de procesamiento estático, las clases dinámicas tienen muchos beneficios.
No hay necesidad de escribir una clase de encapsulación completamente idéntica para el tema real. Si hay muchos métodos en la interfaz del tema, también es problemático escribir un método proxy para cada interfaz. Si la interfaz cambia, el tema real y las clases de proxy deben modificarse, lo que no es propicio para el mantenimiento del sistema;
El uso de algunos métodos dinámicos de generación de proxy puede incluso formular la lógica de ejecución de la clase proxy en tiempo de ejecución, mejorando en gran medida la flexibilidad del sistema.
Hay muchas maneras de generar proxy dinámico: el JDK viene con proxy dinámico, cglib, javassist, etc. Estos métodos tienen sus propias ventajas y desventajas. Este artículo explora principalmente el uso de proxy dinámico y análisis de código fuente en JDK.
Aquí hay un ejemplo para explicar el uso del proxy dinámico en JDK:
clase pública DynamicProxy implementa InvocationHandler {private RealSubject = null; Public Object Invoke (Object proxy, método método, objeto [] args) {if (realSubject == null) {realSubject = new RealSubject (); } método.invoke (RealSubject, Args); return RealSubject; }}Ejemplo del código del cliente
Cliente de clase pública {public static void main (strings [] args) {somett = (somett) proxy.newinstance (classLoader.getSystemLoader (), realSubject.class.getInterfaces (), new DynamicProxy ()); Sujeto.sayhello (); Sujeto.saygoodbye (); }}Como se puede ver en el código anterior, necesitamos usar proxy dinámico en JDK. Use el método estático proxy.newinstance (classloader, interfaces [], invokeHandler) para crear una clase de proxy dinámica. El método NewInstance tiene tres parámetros, que representan el cargador de clases, una lista de interfaces que desea que se implementen la clase proxy y una instancia que implementa la interfaz InvokeHandler. El proxy dinámico entregó el proceso de ejecución de cada método al método de invocación para el procesamiento.
JDK Dynamic Proxy requiere que el proxy sea una interfaz, pero una clase simple no puede. Las clases de proxy generadas por JDK Dynamic Proxy heredarán la clase Proxy, y la clase Proxy implementará toda la lista de interfaz en la que pasó. Por lo tanto, el tipo se puede emitir al tipo de interfaz. A continuación se muestra el diagrama de estructura del proxy.
Se puede ver que el proxy son todos los métodos estáticos, por lo que si la clase proxy no implementa ninguna interfaz, entonces es el tipo de proxy y no tiene métodos de instancia.
Por supuesto, si se une, debe proxy de una clase que no implementa una determinada interfaz, y los métodos de esta clase son los mismos que los definidos por otras interfaces, y se puede implementar fácilmente utilizando la reflexión.
clase pública DynamicProxy implementa InvokeHandler {// La clase que desea proxy de TargetClass private TargetClass = null; // Inicializar esta clase Public DynamicProxy (TargetClass TargetClass) {this.targetClass = TargetClass; } public Object Invoke (Object proxy, método método, objeto [] args) {// Use la reflexión para obtener la clase que desea proxy método mymethod = targetClass.getClass (). mymethod.setAccessible (verdadero); return mymethod.invoke (TargetClass, Args); }}4. Análisis del código fuente de proxy dinámico JDK (JDK7)
Después de mirar el ejemplo anterior, solo sabemos cómo usar proxy dinámico. Sin embargo, todavía está niebla sobre cómo se crea la clase de poder, que llamó el método de Invoke, etc. el siguiente análisis
1. ¿Cómo se crean los objetos proxy?
Primero mire el código fuente del método proxy.newinstance:
Public static Object NewProxyInstance (ClassLoader Loader, class <?> [] interfaces, InvocationHandler H) lanza ilegalArgumentException {} // Obtener información de interfaz Final Clase <?> [] intfs = interfaces.clone (); Final SecurityManager SM = System.GetSecurityManager (); if (sm! = null) {checkProxyAccess (Reflection.getCallerClass (), Loader, INTFS); } // Generar la clase proxy clase <?> Cl = getProxyClass0 (cargador, intfs); // ... ok, veamos la primera mitad primero}Desde el código fuente, se puede ver que la generación de clases proxy depende del método GetProxyClass0. A continuación, echemos un vistazo al código fuente GetProxyClass0:
Clase estática privada <?> getProxyClass0 (cargador de carga de clases, class <?> ... interfaces) {// El número de listas de interfaz no puede exceder 0xffff if (interfaces.length> 65535) {lanzar nueva ilegalArgumentException ("Límite de interfaz excedido"); } // Tenga en cuenta aquí, la siguiente explicación se da en detalle para devolver proxyClassCache.get (cargador, interfaces); } La explicación de proxyClassCache.get es: si la clase proxy que implementa la lista de interfaz ya existe, entonces tómelo directamente desde el caché. Si no existe, uno se genera a través de proxyClassFactory.
Antes de mirar el código fuente de proxyClassCache.get, comprendamos brevemente ProxyClassCache:
Private static final Weakcache <classLoader, class <?> [], clase <? >> proxyClassCache = new WeakCache <> (new KeyFactory (), new ProxyClassFactory ());
ProxyClassCache es un caché de tipo WeakCache. Su constructor tiene dos parámetros. Uno de ellos es el proxyClassFactory utilizado para generar la clase proxy. El siguiente es el código fuente de proxyClassCache.get:
Clase final WeakCache <k, P, V> {... public v get (k key, p parámetro) {}}Aquí K representa la clave, p representa parámetros, v representa el valor
public v get (k key, par parámetro) {// método de juicio nulLobject Java7, si el parámetro está vacío, se lanzará una excepción con el mensaje especificado. Si no está vacío, regrese. Objects.RequirenonNull (parámetro); // Limpiar la estructura de datos de Deakhashmap que contiene referencias débiles, que generalmente se usa para almacenar en caché ExungestaleEnries (); // Obtener cachekey del objeto de cola cachekey = cachekey.valueOf (clave, refqueue); // Llena el proveedor con carga perezosa. Concurrente es un mapa seguro de hilo concurrentMap <objeto, proveedor <v>> valoresmap = map.get (cachekey); if (valueMap == null) {concurrentMap <object, proveedor <v>> oldValuesMap = map.putifabsent (cachekey, valoresmap = new concurrenthashmap <> ()); if (OldValuesMap! = NULL) {ValuesMap = OldValuesMap; }} // Crear subcepita y recuperar el posible proveedor <v> almacenado por eso // Subkey desde ValuesMap Object Subkey = Objects.RequirenonNull (SubkeyFactory.Apply (Key, Parameter)); Proveedor <v> proveedor = valoresmap.get (subpuesta); Fábrica de fábrica = nulo; while (true) {if (proveedor! = null) {// Obtener valor del proveedor. Este valor puede ser una realización de fábrica o caché. // Las siguientes tres oraciones son el código central, que devuelve la clase que implementa InvokeHandler y contiene la información requerida. V valor = proveedor.get (); if (value! = null) {value de retorno; }} // de lo contrario no hay proveedor en caché // o un proveedor que devolvió nulo (puede ser un cachevalue // o una fábrica que no tuvo éxito al instalar el Cachevalue) // El siguiente proceso es el proceso de llenar el proveedor si (factory == null) {// crea una fábrica} si (proveedor == null) {// el suministro} de los demás} La función de While Loop es obtener continuamente la clase que implementa InvokeHandler. Esta clase se puede obtener de la caché o generarse a partir de proxyFactoryClass.
La fábrica es una clase interna que implementa la interfaz del proveedor <v>. Esta clase anula el método GET, y se llama a un método de instancia de tipo proxyFactoryClass en el método get. Este método es la forma real de crear una clase proxy. Veamos el código fuente del método ProxyFactoryClass#Aplicar:
clase pública <?> Aplicar (ClassLoader Loader, clase <?> [] interfaces) {map <class <?>, boolean> interfaceset = new IdentityHashMap <> (interfaces.length); for (clase <?> intf: interfaces) { /* Verifique que el cargador de clase resuelva el nombre de esta interfaz al mismo objeto de clase.* / class <?> InterfaceClass = null; Pruebe {// Cargue información sobre cada interfaceclass = class.forname (intf.getName (), false, cargador); } Catch (ClassNotFoundException e) {} // Si la clase cargada con su propia carga de clase no es igual a la clase en la que pasó, arroje una excepción if (InterFACECLASS! = INTF) {arroje nuevo IlegalArGumentException (intf + "no es visible desde el cargador de clase"); } // Si el entrante no es un tipo de interfaz if (! Interfaceclass.isinterface ()) {tire nueva ilegalArgumentException (interfaceclass.getName () + "no es una interfaz"); } // Verifique si la interfaz se repite if (interfaceset.put (interfaceclass, boolean.true)! = Null) {throw newleLargumentException ("Interfaz repetida:" + interfaceclass.getName ()); }} Cadena proxypkg = null; // paquete para definir la clase proxy en /* Registre el paquete de una interfaz proxy no pública para que la clase proxy se define en el mismo paquete. * Verifique que todas las interfaces proxy no públicas estén en el mismo paquete. */// Este párrafo depende de si hay interfaces que no son públicas en la interfaz en la que pasó. Si es así, todas estas interfaces deben definirse en un paquete. De lo contrario, arroje excepciones para (class <?> Intf: interfaces) {int flags = intf.getModifiers (); if (! Modifier.ispublic (flags)) {String name = intf.getName (); int n = name.lastIndexof ('.'); Cadena pkg = ((n == -1)? "": Name.substring (0, n + 1)); if (proxypkg == null) {proxypkg = pkg; } else if (! pkg.equals (proxypkg)) {arrojar nueva ilegalargumentException ("interfaces no públicas de diferentes paquetes"); }}}} if (proxypkg == null) {// Si no hay interfaces proxy no públicas, use com.sun.proxy paquete proxypkg = reflectUtil.proxy_package + "."; } / * * Elija un nombre para la clase proxy para generar. */ long num = nextUniqueNumber.getAndIncrement (); // Generar el nombre de clase de la clase Proxy aleatoria, $ proxy + num string proxyname = proxypkg + proxyClassNamePrefix + num; /** Genere el archivo de clase de la clase proxy, devuelva la transmisión de byte*/ byte [] proxyClassfile = proxyGenerator.GenerateProxyClass (proxyname, interfaces); Pruebe {return DefinClass0 (cargador, proxyname, proxyClassFile, 0, proxyClassFile.length); } Catch (ClassFormaterror e) {// end tirar nueva ilegalArgumentException (e.ToString ()); }}}ProxyFactoryClass#mencionado mencionado anteriormente es un método para generar realmente clases de proxy, que en realidad es inexacta. Después de leer el código fuente aquí, encontraremos que ProxyGenerator#GenerateProxyClass es el método para generar realmente clases de proxy. Genere el archivo de clase correspondiente de acuerdo con la composición de Bytecode de clase Java (consulte mi otro artículo Notas de aprendizaje de Bytecode Java). El código fuente específico de ProxyGenerator#GenerateProxyClass es el siguiente:
byte privado [] generateclassfile () { / * * Paso 1: ensamble los objetos proxymethod para todos los métodos para * generar código de envío proxy para. */ // El método addProxyMethod es agregar todos los métodos a una lista y corresponder a la clase correspondiente // Aquí hay tres métodos correspondientes al objeto, tostring e igual a addProxyMethod (hashcodemethod, object.class); addProxyMethod (igualsmethod, object.class); addProxyMethod (toStringMethod, object.class); // Compare la interfaz en la lista de interfaz con los métodos en la interfaz 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 cada conjunto de métodos proxy con la misma firma, * verifique que los tipos de retorno de los métodos sean compatibles. */ for (lista <proxymethod> signmethods: proxymethods.values ()) {checkreturnTypes (sigmethods); } / * * Paso 2: Ensamble FieldInfo y MethodInfo Structs para todos los * campos y métodos en la clase que estamos generando. */// Agregue un método de constructor al método, que es solo un constructor, que es un constructor con la interfaz InvocationHandler.///este es el método real para agregar un método al archivo de clase, es decir, la clase proxy. Sin embargo, aún no se ha procesado. Se acaba de agregar primero y espere el bucle. La descripción del nombre del constructor en el archivo de clase es <Init> try {Methods.Add (GenerateConstructor ()); For (List <S ProXyMethod> SignMethods: proxyMethods.values ()) {for (proxymethod pm: signmethods) {// Agregue un atributo de tipo de método a cada método proxy. El número 10 es el identificador del archivo de clase, lo que significa que estos atributos son campos.add (new FieldInfo (pm.methodfieldName, "ljava/lang/reflej/método;", acc_private | acc_static)); // Agregar cada método proxy al método de método de clase proxy.Add (pm.generatemethod ()); }} // Agregue un bloque de inicialización estática e inicialice cada atributo. Aquí, el bloque de código estático también se llama constructor de clase. En realidad, es un método con el nombre <Clinit>, así que agréguelo a los métodos de la lista de métodos. } Catch (ioException e) {tire nuevo internalErsor ("excepción de E/S inesperada"); } // El número de métodos y atributos no puede exceder el 65535, incluido el número anterior de interfaces. // Esto se debe a que en el archivo de clase, estos números se representan en hexadecimal de 4 bits, por lo que el valor máximo es 2 para la potencia de 16th -1 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"); } // El siguiente paso es escribir un archivo de clase, incluidos números mágicos, nombres de clases, grupos constantes y otras series de byteCodes. No entraré en detalles. Si lo necesita, puede consultar el conocimiento relevante de JVM Virtual Machine Bytecode. cp.getClass (dottostore (className)); cp.getClass (superclassname); for (int i = 0; i <interfaces.length; i ++) {cp.getClass (dottostore (interfaces [i] .getName ())); } cp.setReadonly (); BytearRayOutputStream Bout = new ByteArRaReOutputStream (); DataOutputStream Dout = new DataOutputStream (BOUT); intente {// u4 magia; dout.WriteInt (0xCafebabe); // U2 minor_version; dout.writeshort (classfile_minor_version); // U2 Major_version; dout.writeshort (classfile_major_version); cp.write (dout); // (escribir grupo constante) // u2 access_flags; dout.writeshort (acc_public | acc_final | acc_super); // U2 this_class; dout.writeshort (cp.getclass (dottostors (className))); // u2 super_class; dout.writeshort (cp.getclass (superclassname)); // U2 interfaces_count; dout.Writeshort (interfaces.length); // interfaces U2 [interfaces_count]; for (int i = 0; i <interfaces.length; i ++) {dout.writeshort (cp.getClass (dottostore (interfaces [i] .getName ()))); } // U2 Fields_Count; dout.Writeshort (Fields.Size ()); // FIELD_INFO FIELDS [FIELDS_COUNT]; para (fieldinfo f: campos) {f.write (dout); } // u2 métodos_count; dout.writeshort (métodss.size ()); // métodos método_info [métodos_count]; para (MethodInfo M: Methods) {M.Write (Dout); } // u2 atributes_count; dout.Writeshort (0); // (sin atributos de ClassFile para las clases de proxy)} Catch (IOException e) {Throw New GonalSor ("Excepción de E/S inesperada"); } return bout.tobytearray (); }Después de capas de llamadas, finalmente se genera una clase de proxy.
2. ¿Quién llamó a Invoke?
Simulamos JDK para generar una clase proxy por sí misma, con el nombre de clase TestProxygen:
Public Class TestGeneratorProxy {public static void main (String [] args) lanza IOException {byte [] classFile = proxyGenerator.GenerateProxyClass ("testProxygen", somet.class.getinterfaces ()); Archivo archivo = nuevo archivo ("/usuarios/yadoao/escritorio/testProxygen.class"); FileOutputStream fos = new FileOutputStream (archivo); fos.write (classfile); fos.flush (); fos.close (); }}Descompilar el archivo de clase con JD-GUI, y el resultado es el siguiente:
import com.su.dynamicproxy.isubject; import java.lang.reflect.invocationHandler; import java.lang.reflect.method; import java.lang.reflect.proxy; import java.lang.reflect.undecleedthrowableException; pública clase final TestProxygen Extends Expertences Isubject statics statateT. Método estático privado M1; Método estático privado M0; Método estático privado M4; Método estático privado M2; public testProxyGen (InvocationHandler ParamInVocationHandler) lanza {super (paraminVocationHandler); } public final void sayshello () lanza {try {this.h.invoke (this, m3, null); devolver; } Catch (Error | RuntimeException localError) {lanzar localError; } Catch (lanzable localThrowable) {lanzar una nueva no defensa de la concepción (localThrowable); }} public Final Boolean iguales (objeto ParamObject) lanza {try {return ((boolean) this.h.invoke (this, m1, nuevo objeto [] {paramObject})). booleanValue (); } Catch (Error | RuntimeException localError) {lanzar localError; } Catch (lanzable localThrowable) {lanzar una nueva no defensa de la concepción (localThrowable); }} public final int hashcode () lanza {try {return ((integer) this.h.invoke (this, m0, null)). intValue (); } Catch (Error | RuntimeException localError) {lanzar localError; } Catch (lanzable localThrowable) {lanzar una nueva no defensa de la concepción (localThrowable); }} public Final void saysgoodbye () lanza {try {this.h.invoke (this, m4, null); devolver; } Catch (Error | RuntimeException localError) {lanzar localError; } Catch (lanzable localThrowable) {lanzar una nueva no defensa de la concepción (localThrowable); }} public Final String ToString () lanza {try {return (string) this.h.invoke (this, m2, null); } Catch (Error | RuntimeException localError) {lanzar localError; } Catch (lanzable localThrowable) {lanzar una nueva no defensa de la concepción (localThrowable); }} static {try {m3 = class.forname ("com.su.dynamicproxy.isubject"). getMethod ("sayhello", nueva clase [0]); m1 = class.forname ("java.lang.object"). getMethod ("igual", nueva clase [] {class.forname ("java.lang.object")}); m0 = class.forname ("java.lang.object"). getMethod ("hashcode", nueva clase [0]); m4 = class.forname ("com.su.dynamicproxy.isubject"). getMethod ("saygoodbye", nueva clase [0]); m2 = class.forname ("java.lang.object"). getMethod ("toString", nueva clase [0]); devolver; } Catch (nosuchmethodexception localNoSuchMethodException) {tire nuevo nosuchmethoderror (localnosuchmethodexception.getMessage ()); } Catch (ClassNotFoundException localClassNotFoundException) {lanzar newlassDefFoundError (localClassNotFoundException.getMessage ()); }}} Primero, noté que el constructor de la clase proxy generada se pasa en una clase que implementa la interfaz InvokeHandler como un parámetro, y se llama el constructor del proxy de la clase principal, que inicializó la variable de miembro protegida a Invokehander H en proxy.
Noté varios bloques de inicialización estática nuevamente. El bloque de inicialización estática aquí es inicializar la lista de interfaz proxy y los métodos de hashcode, toString e igual.
Finalmente, existe el proceso de llamada de estos métodos, todos los cuales son devoluciones de llamada al método de Invoke.
Esto termina con el análisis de este patrón proxy.