Prefacio
"Agente dinámico" en realidad se origina en el modo proxy en el modo de diseño, y el modo proxy utiliza el objeto proxy para completar las solicitudes de usuario y bloquea el acceso de los usuarios a objetos reales.
Para dar el ejemplo más simple, queremos "FQ" para acceder a sitios web extranjeros, porque no tenemos todas las IPS extranjeras, puede enviar su datagrama de solicitud a aquellos hosts extranjeros que no están bloqueados, y luego reenvía la solicitud al destino configurando el host extranjero y reenviarlo a nuestro host nacional después de recibir el mensaje de respuesta.
En este ejemplo, un anfitrión extranjero es un objeto proxy, y los hosts que son dejados caer por la pared son objetos reales. No podemos acceder directamente a los objetos reales, pero podemos acceder a él indirectamente a través de un proxy.
Una ventaja del modo proxy es que todas las solicitudes externas pasan a través del objeto proxy, y el objeto proxy tiene derecho a controlar si se le permite acceder realmente al objeto real. Si hay una solicitud ilegal, el objeto de poder puede rechazarlo por completo sin realmente problemas con el objeto real.
Una de las aplicaciones más típicas del modo proxy es el marco Spring. AOP de Spring utiliza una programación orientada a los aspectos para aislar la lógica comercial real de las excepciones de registro relacionadas y otra información. Cada vez que solicita la lógica comercial, corresponde a un objeto proxy. Además de realizar las verificaciones de permisos y la impresión de registros necesarias, este objeto proxy es el bloque de procesamiento de lógica comercial real.
Proxy estático
Hay dos implementadores principales del modelo proxy, "proxy estático" y "proxy dinámico". La diferencia esencial entre estos dos es que la primera clase proxy requiere una codificación manual de los programadores, mientras que la última clase proxy se genera automáticamente. Entonces, es por eso que apenas has oído hablar del concepto de "proxy estático". Por supuesto, es naturalmente más fácil entender el "proxy dinámico".
Una cosa que debe ser clara es que el objeto proxy proxy en todos los métodos del objeto real, es decir, el objeto proxy debe proporcionar al menos el mismo nombre del método que el objeto real para la llamada, por lo que un objeto proxy debe definir todos los métodos propiedad del objeto real, incluidos los métodos en la clase principal.
Veamos un ejemplo de proxy estático simple:
Para ilustrar el problema, definimos una interfaz IService y dejamos que nuestra clase real herede e implemente la interfaz, para que haya dos métodos en nuestra clase real.
Entonces, ¿cómo se debe definir una clase de proxy para completar el proxy de objetos reales?
En términos generales, la esencia de una clase proxy es definir todos los métodos en la clase real y agregar algunas otras operaciones dentro del método, y finalmente llamar al método de la clase real.
La clase proxy necesita proxy todos los métodos en la clase real, es decir, los métodos que son exactamente los mismos que los de los métodos en la clase real, y el método de la clase real se llamará indirectamente dentro de estos métodos.
En términos generales, la clase proxy elegirá heredar directamente todas las interfaces y clases de padres de la clase real para obtener todas las firmas de métodos principales de la clase real, es decir, primero estimar todos los métodos principales.
A continuación, el proxy no es un método principal en la clase real. En el ejemplo aquí, el método de doservicio es el método propio de la clase real. Nuestra clase de proxy también necesita definir un método con la misma firma de método para proxy.
De esta manera, incluso si se completa nuestra clase proxy, todos los métodos en la clase real se pueden proxy a través de la clase proxy en el futuro. Como esto:
public static void main (string [] args) {realClass realClass = new RealClass (); ProxyClass proxyClass = new ProxyClass (RealClass); proxyclass.sayhello (); proxyclass.doservice ();}ProxyClass, como objeto de clase proxy, puede representar todos los métodos en la clase real e imprimir información "insignificante" antes de ejecutar estos métodos.
Esta es básicamente la idea de implementación básica del modelo proxy, pero la diferencia entre el proxy dinámico y este tipo de proxy estático es que el proxy dinámico no requiere nuestras definiciones de métodos una por una, y la máquina virtual generará automáticamente estos métodos para usted.
Mecanismo de proxy dinámico JDK
Lo que distingue el proxy dinámico del proxy estático es que la clase proxy de proxy dinámica es creada dinámicamente por la máquina virtual en tiempo de ejecución y se borra cuando la máquina virtual no está instalada.
Reutilizamos las clases utilizadas en el proxy estático anterior para ver cómo el proxy dinámico de JDK puede generar todos los métodos de una instancia de una determinada clase.
Definir una clase de procesamiento de controladores:
La API proxy dinámica en la función principal llama a JDK para generar una instancia de clase proxy:
Todavía hay bastante código involucrado, analicémoslo poco a poco. Primero, RealClass implementa el servicio de interfaz como nuestra clase de proxy y define su propio doservicio de método internamente.
A continuación, definimos una clase de procesamiento que hereda la interfaz InvocationHandler e implementa su método de invocación declarado exclusivamente. Además, tenemos que declarar un campo miembro para almacenar objetos reales, es decir, objetos proxy, porque cualquier método que proxy se basa básicamente en métodos relevantes de objetos reales.
Con respecto al papel de este método de invocación y la importancia de varios parámetros formales, realizaremos un análisis detallado cuando reflejemos el código fuente proxy.
Finalmente, defina nuestra clase de procesamiento y básicamente realice proxy dinámico basado en JDK. El método central es el método NewProxyInstance de la clase proxy. Este método tiene tres parámetros. Uno es un cargador de clase, el segundo es una colección de todas las interfaces implementadas por la clase proxy, y la tercera es nuestra clase de procesador personalizada.
La máquina virtual utiliza el cargador de clase que proporciona en tiempo de ejecución, carga todas las clases de interfaz especificadas en el área de método y luego refleja y lee los métodos en estas interfaces y combina la clase de procesador para generar un tipo proxy.
La última oración puede ser un poco abstracta. ¿Cómo "combinado con clases de procesadores para generar un tipo proxy"? En este sentido, especificamos los parámetros de inicio de la máquina virtual y lo dejamos guardar el archivo de clase generado de la clase proxy.
-Dsun.misc.proxyGenerator.saveGeneratedFiles = True
Descompilaremos este archivo de clase a través de herramientas de terceros, y hay mucho contenido. Dividimos el análisis:
En primer lugar, el nombre de esta clase de poder es muy aleatorio. Si hay múltiples clases de proxy que se generarán en un programa, "$ proxy + número" es su nombre de clase.
A continuación, notará que esta clase de proxy hereda la clase proxy y la interfaz iservice que especificamos (previamente si se especificaran múltiples interfaces, se heredarían múltiples interfaces múltiples aquí).
Luego encontrará que este constructor requiere un parámetro de tipo Invocathandler, y el cuerpo del constructor debe pasar esta instancia de InvocationHandler al campo correspondiente del proxy de la clase principal para el almacenamiento. Esta es también una de las razones por las cuales todas las clases de proxy deben usar proxy como clase principal, que es publicitar el campo InvocationHandler en la clase principal. Más tarde, sabremos que este pequeño diseño conducirá a una desventaja fatal del proxy dinámico basado en JDK, que se introducirá más adelante.
Este contenido también es una parte relativamente importante de la clase proxy. Se ejecutará cuando la máquina virtual inicialice estáticamente la clase proxy. Esta gran pieza de código completa la función de reflejar todos los métodos en la interfaz, y todos los métodos reflejados se almacenan correspondientes a un campo de tipo de método.
Además, la máquina virtual también refleja tres métodos comunes en el objeto, es decir, la clase proxy también proxyará el objeto real heredado del objeto.
En la última parte, lo que vemos es que la máquina virtual refleja todos los métodos que se proxyan en función del bloque de código de inicialización estática y genera métodos proxy para ellos.
Estos métodos parecen mucho código, pero de hecho son solo una línea de código, que elimina la clase de procesador almacenada durante la instancia del proxy de la clase principal y llama a su método de invocación.
Los parámetros del método son básicamente los mismos. El primer parámetro es la instancia de clase proxy actual (demuestra que es inútil pasar este parámetro en el pasado), el segundo parámetro es la instancia del método del método, y el tercer parámetro es el conjunto de parámetros formales del método. Si no, es nulo.
Ahora echemos un vistazo a la clase de procesador personalizada:
Todos los métodos de clase proxy llamarán al método de invocación de la clase de procesador y pasarán el método actual de la clase proxy. Este método de invocación puede optar por hacer que el método se llame normalmente, o omitir la llamada del método, e incluso hacer algunas cosas adicionales antes y después de que se llame realmente el método.
Esta es la idea central del proxy dinámico JDK. Resumamos brevemente todo el proceso de llamadas.
En primer lugar, la definición de una clase de procesador es esencial, y debe estar asociada con un objeto real, es decir, la instancia de clase proxy.
A continuación, llamamos a cualquier método de la clase proxy desde el exterior, y desde el código fuente descompilado, sabemos que el método de clase proxy llamará al método de invocación del procesador y pasará el conjunto de parámetros formales de firma y método de firma y método.
Finalmente, si el método se puede llamar normalmente depende de si el procesador invoca el cuerpo del método realmente llama al método del método.
De hecho, el proxy dinámico implementado basado en JDK es defectuoso, y estos defectos no son fáciles de solucionar, por lo que CGLIB es popular.
Algunos defectos y deficiencias
Mecanismo de proxy único
No sé si notaste que los ejemplos anteriores no están disponibles. La clase proxy generada por la máquina virtual hereda la clase proxy para almacenar públicamente sus propias instancias de clase de procesador. ¿Qué significa eso?
La herencia de raíz única de Java le dice que la clase proxy ya no puede heredar ninguna otra clase, por lo que los métodos en la clase principal de la clase proxy, naturalmente, no podrán obtener, es decir, la clase proxy no puede proxy ningún método de la clase principal en la clase real.
Aparte de esto hay otro pequeño detalle. Me pregunto si lo has notado. Lo escribí así.
El método Sayhello aquí es la interfaz IService implementado, mientras que el método de doservicio es un método que pertenece a RealClass '. Pero no vemos este método de la clase proxy, lo que significa que este método no está proxyed.
Por lo tanto, el mecanismo de proxy dinámico de JDK es único, y solo puede proxie métodos en la colección de interfaz de la clase proxy.
Valor de retorno hostil
Tenga en cuenta que NewProxyInstance devuelve una instancia de la clase Proxy "$ proxy0", pero se devuelve como el tipo de objeto, y no puede forzar la instancia de objeto al tipo "$ proxy0".
Aunque sabemos que esta instancia de objeto es en realidad el tipo "$ proxy0", el tipo "$ proxy0" no existe durante el período de compilación, y el compilador naturalmente no le permitirá forzarlo a un tipo inexistente. Por lo tanto, generalmente solo forzará que sea una de las interfaces implementadas por esta clase de proxy.
RealClass rc = new RealClass (); myhanlder hanlder = new myhanlder (rc); iservice obj = (iservice) proxy.newproxyInstance (rc.getClass (). getClassLoader (), nueva clase [] {iservice.class}, hanlder); obj.sayhello ();Salida de ejecución del programa:
Proxy comienzo ...... hola mundo ... final proxy ......
Entonces la pregunta llega de nuevo. Si nuestra clase proxy implementa múltiples interfaces, ¿a qué tipo de interfaz debe forzarla? Ahora, suponiendo que la clase proxy implementa las interfaces A y B, entonces si la última instancia se ve obligada a A, naturalmente, no puede llamar a todos los métodos en la interfaz B implementados por la clase proxy y viceversa.
Esto conduce directamente a un resultado. Debe saber qué método hay en qué interfaz. Si lo obliga a la interfaz correspondiente antes de llamar a un método, es bastante hostil.
Lo anterior es lo que creemos que no es elegante en el mecanismo de proxy dinámico basado en JDK. Por supuesto, sus ventajas son definitivamente mayores que estas desventajas. En el próximo artículo, presentaremos una biblioteca proxy dinámica CGLIB ampliamente utilizada por varios marcos. Su capa subyacente se basa en el marco de operación de bytecode ASM y ya no se basa en la herencia para implementarla, resolviendo perfectamente las deficiencias del proxy único de JDK.
Todos los códigos, imágenes y archivos en el artículo se almacenan en la nube en mi github:
(https://github.com/singleyam/overview_java)
También puede elegir descargar localmente.
Resumir
Lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo tenga cierto valor de referencia para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.