Modo de diseño (15) Modo estructural-Modo de agente: estático, agente dinámico, agente Cglib

Prólogo

El patrón estructural del patrón de diseño es el último y el más difícil (lo siento durante el proceso de aprendizaje): el
patrón de agente. El conocimiento previo del aprendizaje del patrón de agente: reflexión de Java

Primero revise los patrones estructurales anteriores:

  1. Modo de adaptador: cree una clase de adaptador y convierta la clase que el cliente no puede usar en la clase que el cliente necesita (cargador: 220V-> 5V)
  2. Modo puente: construya un "puente" por medio de la agregación, y simplemente combine los cambios de múltiples dimensiones de una cosa (la pintura requiere múltiples colores y modelos de tamaño de pincel)
  3. Modo de decoración: "Patrón de muñeca", que presenta a la persona modificada al decorador y agrega dinámicamente funciones a la persona modificada (agregue azúcar y leche al café)
  4. Modo de combinación: idea recursiva, heredando la misma clase de múltiples niveles de la estructura de árbol de las necesidades del cliente, tratando objetos de contenedor (nodos superiores) y objetos de hoja (nodos de hoja) consistentemente, manteniendo un contenedor para almacenar nodos inferiores en el objeto de contenedor, recursivamente Estructura de árbol (descripción de varios niveles del directorio de Linux)
  5. Modo de apariencia: establezca una función de apariencia para concentrar las funciones del subsistema, de modo que la clase cliente simplemente pueda llamar al método de función de apariencia para lograr funciones complejas del subsistema (un control remoto para cine en casa puede controlar varios instrumentos)
  6. Modelo Flyweight: la fábrica Flyweight mantiene un grupo Flyweight para garantizar la reutilización de roles compartidos específicos (String y pool constante)

Este artículo: el concepto de modo proxy- "proxy estático-" proxy dinámico- "proxy Cglib-" resumen

Problemas en la realidad

Un cliente no quiere o no puede referirse directamente a un objeto, en este caso, se puede lograr una referencia indirecta a través de un tercero llamado "proxy"

El objeto proxy puede actuar como intermediario entre el cliente y el objeto de destino , y puede eliminar contenido y servicios que el cliente no puede ver o agregar servicios adicionales que el cliente necesita a través del objeto proxy

El modo proxy es muy común, por ejemplo:

Tome un tren cuando viaje, no tiene que ir a la estación de tren para comprar un boleto de tren
Inserte la descripción de la imagen aquí

Puede comprarlo a través del sitio web 12306 o ir a la agencia de boletos de tren.

Este es el modo proxy: el cliente no quiere ir directamente a la estación de tren (usando un objeto), pero compra un boleto en 12306 (usando el rol de agente para llamar al método del objeto original)

Modo proxy

Patrón de proxy : proporcione un proxy para un objeto, y el objeto proxy controla la referencia al objeto original. El modo proxy en inglés se llama proxy o sustituto

Estructura del modo proxy:
Inserte la descripción de la imagen aquí

El papel del modo proxy:

  • Rol de tema abstracto (Asunto): declara métodos comerciales implementados por objetos reales de tema y proxy a través de interfaces o clases abstractas.
  • Sujeto real: Realiza el negocio específico en el tema abstracto, es el objeto real representado por el objeto proxy y es el objeto al que se hará referencia eventualmente.
  • Rol de proxy (Proxy): proporciona la misma interfaz que el tema real, que contiene referencias al tema real, puede acceder, controlar o ampliar la funcionalidad del tema real.

Un poco similar al modo de apariencia, la diferencia con el modo de apariencia:

El modo de apariencia es un método para concentrar funciones complejas del subsistema en roles de apariencia.Los clientes solo necesitan usar roles de apariencia para completar funciones complejas (como el control remoto del sistema de cine en casa presione: las luces se apagarán, la pantalla se encenderá, el DVD se encenderá) , El proyector está encendido), centrándose en la concentración de la función del subsistema y la simplificación del cliente

El modo proxy es controlar la referencia del objeto original a través del rol del agente para los objetos que el cliente no puede y no quiere usar.

Proxy estático

El proxy estático es relativamente simple, solo tenga en cuenta el caso de compra del boleto

package com.company.Structural.Proxy;
//抽象类:定义火车站功能
interface Station {
    public void buyTicket();
}
//火车站售票处
class TrainStation implements Station{

    @Override
    public void buyTicket() {
        System.out.println(" 购买火车票。。。");
    }
}
//代购火车票:12306
class WebSite12306 implements Station{
    //内置一个火车站对象
    private Station station = new TrainStation();

    @Override
    public void buyTicket() {
        System.out.println(" 打开12306。。。");
        station.buyTicket();
        System.out.println(" 关闭12306。。。");
    }
}
class User{
    public static void main(String[] args) {
        WebSite12306 webSite12306 = new WebSite12306();
        webSite12306.buyTicket();
    }
}

Inserte la descripción de la imagen aquí

Hay un método para comprar boletos de tren dentro de 12306 (objeto de estación de tren incorporado). Los clientes que compran boletos de tren hasta 12306 son esencialmente boletos comprados en la estación de tren, pero solo a través del rol de tercer agente.

Proxy estático a proxy dinámico

Defectos de agentes estáticos.

Obviamente, un proxy estático implementa su propio método combinando un objeto original y luego llamando al método del objeto original. Tal defecto es que un objeto original requiere un objeto proxy, y la extensión necesita modificar el código fuente, que no cumple con el "principio de apertura y cierre"
y el objeto Debe ser conocido para que el objeto pueda combinarse

Por ejemplo, si agrega una oficina de venta de boletos aéreos, el objeto proxy debe tener un objeto incorporado de venta de boletos aéreos, que solo puede agregarse modificando el código fuente o creando un nuevo objeto proxy

La solución a este problema es el proxy dinámico

Proxy dinámico

El proxy dinámico también se llama proxy JDK y proxy de interfaz y
tiene las siguientes características:

  1. El rol de proxy no necesita implementar la interfaz, pero el rol de tema real debe implementar la interfaz (de lo contrario, no se puede usar proxy dinámico)
  2. La generación del objeto proxy se realiza a través de la reflexión en el JDK, y el rol proxy se construye dinámicamente en la memoria.
  3. El paquete donde se encuentra la clase proxy es java.lang.reflect, que implementa el método Proxy.newProxyInstance en la reflexión de Java

Implementación de proxy dinámico

Simule el proceso de compra de boletos en 12306 con agentes dinámicos

package com.company.Structural.Proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//抽象类:定义火车站功能
interface Station {
    public void buyTicket();
}
//火车站售票处
class TrainStation implements Station{

    @Override
    public void buyTicket() {
        System.out.println(" 购买火车票。。。");
    }
}
//代购火车票:12306
class WebSite12306{
    //目标对象
    private Object target;

    public WebSite12306(Object target) {
        this.target = target;
    }

    //生成代理对象
    public Object getProxyInstance() {
        //事件处理,传入目标对象的方法,可以自定义额外方法
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("---------------打开12306---------------");
                Object result = method.invoke(target, args);
                System.out.println("---------------关闭12306---------------");
                return result;
            }
        };
        //通过反射机制
        /*
        * public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
          1. ClassLoader是目标对象的类加载器
          2. interfaces是目标对象实现的接口,用泛型方式确认类型
          3. InvocationHandler是事件处理(自己编写),用于触发事件处理器方法,当前执行的目标对象的方法会作为参数传入
        * */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler);

    }
}
class User{
    public static void main(String[] args) {
        //目标对象
        Station trainStation = new TrainStation();
        //创建代理工厂
        WebSite12306 webSite12306 = new WebSite12306(trainStation);
        //得到代理角色
        Station proxyInstance = (Station) webSite12306.getProxyInstance();
        //执行方法
        proxyInstance.buyTicket();
    }
}

Inserte la descripción de la imagen aquí

Se puede ver que el proxy dinámico obtiene el método del objeto objetivo a través de la reflexión. En este momento, se puede implementar cualquier tipo de objeto objetivo (el objetivo es un objeto de tipo Objeto)

Esto implica el conocimiento de la reflexión, un poco de aprendizaje.

Proxy dinámico relacionado

Obtenemos el objeto de carga de clase del objeto de destino a través de Proxy, implementamos la interfaz y luego escribimos el objeto de procesamiento de eventos para completar el proxy dinámico. Entre estos, debemos prestar atención a:

Proxy.newProxyInstance

Proxy es una clase proxy, una clase que crea dinámicamente un objeto proxy, proporciona algunos métodos
Inserte la descripción de la imagen aquí

Uno de los más utilizados es el método newProxyInstance, que devuelve un objeto proxy dinámico

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

Inserte la descripción de la imagen aquí
Pasó tres parámetros: ClassLoader, Class <?> [] Interfaces, InvocationHandler

ClassLoader sabe que es un objeto cargador de clases, que decide qué cargador usar para procesar el objeto.
Lo que pasamos es target.getClass().getClassLoader()el cargador de clases del objeto de destino

Interfaces es una colección de interfaces implementadas por el objeto de destino. Utilice métodos genéricos para confirmar el tipo Clase <?> []
Este parámetro es un conjunto de interfaces del objeto de destino que se representará . Si le proporciono un conjunto de interfaces, el objeto proxy declara Implementé la interfaz (polimorfismo), para poder llamar a los métodos en este grupo de interfaces, obtuvimos target.getClass().getInterfaces()la interfaz implementada por el objeto de destino

InvocationHandler es una interfaz de procesamiento de eventos, que es un objeto de procesamiento de eventos escrito por nosotros mismos (el método del objeto de destino se incluye aquí)

Inserte la descripción de la imagen aquí
Esta interfaz define una especificación de método de invocación

InvocationHandler

El método de invocación definido en la interfaz InvocationHandler pasa 3 parámetros, y el valor de retorno es Object

 public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

Cuando hay varias clases de proxy, cada clase de proxy se asociará con un controlador. Cuando llamamos a un método a través del objeto proxy, la llamada de este método se reenviará al método de invocación de la interfaz de InvocationHandler para llamar El método de ejecución del objeto proxy será reemplazado por el método de invocación de ejecución

Esta puede ser una buena extensión del sistema de modo proxy (no es necesario preocuparse de qué método se pasa al final)

Echemos un vistazo a estos tres parámetros: proxy de objeto, método de método, argumentos de Object []

Proxy de objeto : como puede ver en el nombre, este es el objeto de destino que necesitamos para proxy

Método de método : el objeto Método para llamar a un método del objeto de destino, proxyInstance.buyTicket();el método buyTicket llamado en el caso anterior es este objeto Método

Se puede ver a través de Debug
Inserte la descripción de la imagen aquí

Contendrá varios atributos del Método

Object [] args : los parámetros pasados ​​en el método anterior y los parámetros necesarios para llamar al objeto de destino. El caso anterior no se muestra, pero puede modificar Debug

Necesita agregar nombre en el método buyTicket
Inserte la descripción de la imagen aquí

Método de ejecución: proxyInstance.buyTicket("zhangsan");
puede ver la cadena "zhangsan" agregada a la matriz args
Inserte la descripción de la imagen aquí

Proxy0

Podemos generar cuál es la clase obtenida por getProxyInstance ().
Inserte la descripción de la imagen aquí
Agregar un método getClass

Inserte la descripción de la imagen aquí

Después de la ejecución, puede ver que el nombre de la clase es: com.company.Structural.Proxy.$Proxy0
¿Por qué se devuelve la clase Proxy. $ Proxy0?

El objeto proxy creado por Proxy.newProxyInstance es un objeto generado dinámicamente cuando se está ejecutando la JVM. El método de denominación comienza con $, el proxy está en el medio y el último número indica la etiqueta del objeto$Proxy0

En cuanto a lo anterior: com.company.Structural.es el nombre de mi paquete
Proxyporque estamos usando el método de la clase Proxy

Mirando nuestro nombre de método esProxy.Station.buyTicket()

Inserte la descripción de la imagen aquí
Esto se debe a que nuestro método se ejecuta desde el método getProxyInstance de la clase proxy al objeto invocar del objeto controlador en el método newProxyInstance y el controlador
acepta el objeto objetivo, lo que indica que el objeto se ejecuta, y se llamará al método invocar en el controlador para ejecutar el objeto objetivo. El método correspondiente

Por lo tanto, el nombre del objeto del Método es: Proxy.Station.buyTicket()
Proxy representa el método newProxyInstance de la clase llamada Proxy, Station es la interfaz que escribimos y es el tipo de nuestro objeto proxy forzado, buyTicket es el método específico utilizado

Tipo de estación

Station proxyInstance = (Station) webSite12306.getProxyInstance();

Cuando obtenemos el objeto proxy, usamos el tipo forzado a Station, ¿por qué podemos forzarlo así?

El parámetro pasado por Proxy.newProxyInstance: Class <?> [] Las interfaces representan la interfaz implementada por el objeto de destino, a través de este parámetro, nuestro objeto proxy también implementará esta interfaz

Es por eso que dije antes: el rol de agente no necesita implementar la interfaz, pero el rol de tema real debe implementar la interfaz

Proxy Cglib

¿Por qué usar proxy Cglib?

Nota: en el proxy dinámico anterior, el objeto de destino debe implementar la interfaz, el objeto proxy implementa la interfaz internamente a través del parámetro de interfaces de Proxy.newProxyInstance

Pero en muchos casos, el objeto de destino no implementa la interfaz, por lo que no se puede usar el proxy dinámico. . .

En este momento, el proxy se puede implementar a través de la subclase del objeto de destino, que es el proxy Cglib

El proxy Cglib también se llama proxy de subclase. Es para construir un objeto de subclase en la memoria para lograr la expansión de la función del objeto de destino . El proxy Cglib también es un proxy dinámico

Cglib es un paquete de generación de código potente y de alto rendimiento. Puede ampliar las clases de Java e implementar interfaces Java durante el tiempo de ejecución. Es utilizado por muchos marcos de trabajo de AOP. Por ejemplo, Spring AOP es la encapsulación de Cglib. El método de implementación intercepta la
capa inferior del paquete de Cglib usando Marco de procesamiento de bytecode ASM para convertir bytecode y generar nuevas clases

Implementación de proxy Cglib

Primero introduce 4 paquetes jar
Inserte la descripción de la imagen aquí

Implementación simple del caso de compra de boletos anterior

package com.company.Structural.Proxy.Cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

class TrainStation{
    public void buyTicket(String name) {
        System.out.println(name+" 购买火车票。。。");
    }
}

class WebSite12306 implements MethodInterceptor {
    //维护一个目标对象
    private Object target;
    //构造器
    public WebSite12306(Object target) {
        this.target = target;
    }
    //返回代理对象
    public Object getProxyInstance(){
        //创建工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(target.getClass());
        //设置回调函数,自己回调
        enhancer.setCallback(this);
        //创建代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("-----------Cglib:12306开启-----------");
        Object result = method.invoke(target, args);
        System.out.println("-----------Cglib:12306关闭-----------");
        return result;
    }
}
class Client{
    public static void main(String[] args) {
        //创建目标对象
        TrainStation trainStation = new TrainStation();
        //获得代理对象
        TrainStation proxyInstance = (TrainStation)new WebSite12306(trainStation).getProxyInstance();
        //执行代理对象方法
        proxyInstance.buyTicket(" zhangsan ");
    }
}

Inserte la descripción de la imagen aquí

Todavía se puede hacer, verifique la secuencia de ejecución a través de Debug

getProxyInstance devuelve el objeto proxy:
Inserte la descripción de la imagen aquí
ejecute el método proxy:

Inserte la descripción de la imagen aquí
Ejecute el método de ejecución reescrito:
Inserte la descripción de la imagen aquí

Y ejecute el método pasado por el objeto de destino

El agente Cglib es similar al agente dinámico, y se ha mejorado sobre la base del agente dinámico.

Seleccione

Qué agente elegir en la programación de AOP:

  1. El objeto de destino necesita implementar la interfaz, usando el proxy JDK
  2. El objeto de destino no necesita implementar una interfaz, use el proxy Cglib

Variante de modo proxy

El proxy estático, el proxy dinámico y el proxy Cglib mencionados anteriormente son los modos proxy más utilizados.

Habrá algunas variantes del modo proxy para diferentes entornos de aplicación (pero esencialmente proxy estático o dinámico)

  1. Proxy de firewall La
    red interna penetra en el firewall a través del proxy para obtener acceso a la red pública.
  2. Proxy de caché: por ejemplo, cuando solicite recursos como archivos de imagen, vaya primero al proxy de caché. Si se recupera el recurso, entonces está bien. Si el recurso no se puede recuperar, vaya a la red o base de datos pública y luego a la caché.
  3. Agente remoto
    El representante local del objeto remoto, a través del cual puede llamar al objeto remoto como un objeto local. Los agentes remotos comunican información con objetos remotos reales a través de la red.
  4. Agente de sincronización: se utiliza principalmente en la programación multiproceso para completar el trabajo de sincronización entre múltiples hilos
  5. Agente virtual: cuando la carga de un objeto consume muchos recursos, las ventajas del agente virtual son muy obvias. El modo proxy virtual es una técnica de ahorro de memoria. Aquellos objetos que ocupan mucha memoria o procesan objetos complejos serán diferidos hasta que se usen.
    Cuando se inicia la aplicación, se pueden usar objetos proxy en lugar de la inicialización de objetos reales, ahorrando uso de memoria y acelerando en gran medida el tiempo de inicio del sistema

Estas variantes se basan en agentes dinámicos y se extienden en diferentes entornos.

Resumen

  • El modo proxy es crear un rol proxy, el cliente no quiere o no puede usar directamente el objeto objetivo, el cliente controla la referencia al objeto objetivo a través del objeto proxy
  • El modo de agente se divide en modo de agente estático y modo de agente dinámico, y el modo de agente dinámico se puede dividir en agente JDK y agente Cglib
  • El proxy estático es la combinación del objeto objetivo dentro del objeto proxy y la llamada interna del método del objeto objetivo. El cliente controla la referencia al objeto objetivo a través del objeto proxy
  • La desventaja del proxy estático es que el objeto de destino debe aclararse y el objeto de destino debe combinarse en el objeto de proxy, que no puede modificarse ni ampliarse (el código fuente del objeto de proxy debe modificarse), y el proxy dinámico resuelve este problema.
  • En el proxy dinámico, el proxy JDK utiliza la reflexión y los proxys de Java. El nuevo método ProxyInstance del Proxy puede obtener el cargador de clases y la interfaz implementada del objeto de destino, y escribir el procesamiento de eventos por sí mismo. Implementar objetos proxy
  • El proxy JDK requiere que el objeto de destino implemente la interfaz (los métodos internos del objeto proxy implementan la interfaz del objeto de destino), pero muchas clases no necesitan implementar la interfaz. Para estas clases, el proxy JDK no puede manejarlo, y se hace a través del proxy Cglib
  • El agente Cglib utiliza el marco de procesamiento de bytecode ASM para procesar bytecode para formar una nueva clase, construir un objeto de subclase del objeto de destino en la memoria y completar la expansión de la función del objeto de destino
  • La mayoría de los marcos de AOP como Spring AOP completan la función AOP a través del proxy Cglib
Publicado 121 artículos originales · ganó 31 · vistas 7869

Supongo que te gusta

Origin blog.csdn.net/key_768/article/details/105371084
Recomendado
Clasificación