Patrones de diseño: comprensión de los agentes estáticos y dinámicos en el patrón de agente


1. Información general

Insertar descripción de la imagen aquí

Proxy Pattern es un patrón de diseño estructural, su concepto es muy simple: controla el acceso al objeto original mediante la creación de un objeto proxy. El modelo de agente implica principalmente dos roles: el rol de agente y el rol real. La clase proxy es responsable de representar la clase real y proporcionar la función de controlar el acceso de la clase real, mientras que la clase real completa la lógica empresarial específica. De esta forma, cuando nos resulta inconveniente o imposible acceder directamente al objeto real, podemos acceder a él indirectamente a través del objeto proxy. Hay dos propósitos principales al usar el modo proxy: uno es proteger el objeto de destino y el otro es mejorar el objeto de destino.

modo proxy01

Consejo: la clase real, la clase original y la clase de destino mencionadas en el artículo tienen el mismo significado y todas se refieren al rol de agente.

1.1 ¿Cómo implementar?

El principio del patrón proxy es encapsular las funciones de la clase real en la clase proxy. La forma más básica es crear una clase proxy. La clase proxy implementa la misma interfaz que la clase real y hace referencia a la instancia de la clase real. clase en la clase proxy. De esta forma, el acceso a la clase real se puede controlar a través de la clase proxy sin modificar el código de la clase real. La clase proxy también puede agregar algo de lógica adicional antes y después de llamar a los métodos de la clase real para implementar control de acceso , almacenamiento en caché, registro y otras funciones para la clase real.

Hay dos opciones para implementar el modo proxy: proxy estático y proxy dinámico. Proxy estático significa que la clase de proxy se ha determinado en el momento de la compilación, es decir, una clase de proxy debe escribirse manualmente con anticipación. El proxy dinámico genera dinámicamente clases de proxy en tiempo de ejecución.

Hay dos implementaciones de soluciones de proxy dinámico: primero, java.lang.reflect.Proxyla función de proxy dinámico se implementa a través de las propias clases de Java. En segundo lugar, se introduce un paquete adicional de generación de código de alto rendimiento, CGlib, de código abierto para generar dinámicamente clases de proxy. La implementación subyacente de ambos utiliza tecnología de código de bytes de reflexión y manipulación.

El proxy dinámico JDK es un proxy basado en la implementación de la interfaz y solo puede representar las clases que implementan la interfaz.

El método CGlib es un proxy basado en herencia, no significa que la clase real deba heredar una determinada clase principal, pero la clase proxy generada actúa como una subclase de la clase real para representar a la clase principal, es decir, el proxy. la clase hereda de la clase real. Este método no requiere la implementación de una interfaz y puede usarse como una solución complementaria al método proxy JDK.

modo proxy03

1.2 Ventajas

  • El objeto proxy puede ocultar los detalles de implementación del objeto original, de modo que el cliente no necesita conocer la implementación específica del objeto original.
  • Los objetos proxy pueden agregar funciones adicionales basadas en el objeto original, como almacenamiento en caché, verificación de seguridad, etc.
  • El objeto proxy puede controlar el acceso al objeto original y proteger el objeto original del acceso ilegal.
  • El objeto proxy puede desempeñar un papel de intermediario entre el cliente y el objeto original, reduciendo el acoplamiento entre el cliente y el objeto original.

1.3 Desventajas

  • La introducción de clases de agentes aumentará la complejidad del sistema y aumentará el costo de aprendizaje y comprensión.
  • Debido a la adición de la capa de proxy, el procesamiento de solicitudes se ralentiza.

1.4 Escenarios aplicables

El modo proxy es principalmente adecuado para escenarios en los que es necesario controlar, mejorar u ocultar el acceso a objetos. Los escenarios específicos adecuados para el acceso proxy son los siguientes:

  • Proxy remoto: cuando un cliente necesita acceder a un objeto remoto (ubicado en un espacio de direcciones o red diferente), el patrón de proxy se puede utilizar para ocultar la complejidad de la comunicación de red subyacente. El objeto proxy es responsable de manejar la comunicación de red y devolver los resultados al cliente.
  • Proxy virtual: cuando crear e inicializar un objeto es costoso, puede usar el patrón de proxy para retrasar la creación de instancias del objeto e inicializarlo solo cuando el objeto realmente sea necesario. Esto mejora el rendimiento del sistema y la utilización de recursos.
  • Proxy de seguridad: el modo proxy se puede utilizar para controlar el acceso a recursos confidenciales. El objeto proxy puede verificar los permisos del cliente o realizar algunas comprobaciones de seguridad antes de acceder al recurso, protegiendo así el objeto real.
  • Proxy de registro: a través del modo proxy, podemos registrar antes y después de la ejecución del método del objeto real para lograr funciones como registro, depuración y monitoreo del rendimiento.
  • Proxy de carga diferida: cuando el objeto que necesita usar tiene una gran sobrecarga, puede usar el modo proxy para implementar la carga diferida y solo cargar el objeto cuando sea realmente necesario para ahorrar recursos y mejorar la velocidad de respuesta.
  • Proxy de almacenamiento en caché: el modo proxy se puede utilizar para implementar el almacenamiento en caché de objetos. Cuando el cliente solicita un objeto, el objeto proxy primero verifica si el objeto existe en el caché. Si existe, regresa directamente. De lo contrario, se crea un nuevo objeto y almacenado en caché, mejorando así el rendimiento del sistema.

2 Implementación de proxy estático

El método de implementación es relativamente simple y fácil de entender: puede ingresar el código directamente.

NO.1 Interfaz abstracta : define la interfaz del reproductor de video.

public interface Player {
    
    
    void loadVideo(String filename);
    void playVideo(String filename);
}

NO.2 Clase real : define la clase de implementación de la interfaz VPlayer

public class VPlayer implements Player {
    
    
    @Override
    public void loadVideo(String filename) {
    
    
        System.out.println("加载MP4视频文件:"+filename);
    }

    @Override
    public void playVideo(String filename) {
    
    
        System.out.println("播放MP4视频:"+filename);
    }
}

NO.3 Clase de proxy : defina la clase de proxy VPlayerProxy para implementar la misma interfaz

public class VPlayerProxy implements Player {
    
    

    private Player player;

    public VPlayerProxy(Player player) {
    
    
        this.player = player;
    }

    @Override
    public void loadVideo(String filename) {
    
    
        player.loadVideo(filename);
    }

    @Override
    public void playVideo(String filename) {
    
    
        player.playVideo(filename);
    }
}

NO.4 Llamada del cliente

public class Client1 {
    
    
    public static void main(String[] args) {
    
    
        //直连方式
        Player vplay=new VPlayer();
        vplay.playVideo("aaa.mp4");
        System.out.println();

        //代理方式
        Player proxy=new VPlayerProxy(vplay);
        proxy.loadVideo("aaa.mp4");
        proxy.playVideo("aaa.mp4");

    }
}

imagen-20230610194243722

3 Implementación de proxy dinámico JDK

El proxy dinámico JDK es un método proxy proporcionado en la biblioteca estándar de Java. Puede generar dinámicamente un objeto proxy en tiempo de ejecución. El objeto proxy implementa la misma interfaz que la clase original y reenvía llamadas al método al objeto proxy. Al mismo tiempo, También puede realizar un procesamiento de mejora adicional antes y después de las llamadas a métodos. El proxy dinámico JDK implementa la función de proxy a través del mecanismo de reflexión y su principio se divide en los siguientes pasos:

  1. Cree una fábrica de clases de proxy que implemente la interfaz InvocationHandler: al llamar al método estático newProxyInstance de la clase Proxy, se generará dinámicamente una clase de proxy. La clase de proxy implementa la interfaz de destino y contiene una referencia de tipo InvocationHandler.
  2. Interfaz InvocationHandler: InvocationHandler es una interfaz con un solo método de invocación. Cuando se llama al método del objeto proxy, la JVM llamará automáticamente al método de invocación de la clase proxy y pasará el nombre del método llamado, los parámetros y otra información al método.
  3. Llamar al método del objeto proxy: cuando se llama al método del objeto proxy, la JVM llamará automáticamente al método de invocación de la clase proxy. En el método de invocación, se pueden ejecutar diversas lógicas según sea necesario, como agregar registros, estadísticas de rendimiento, gestión de transacciones, etc.
  4. Llamada al método de invocación: en el método de invocación, el método del objeto de destino se llama a través del mecanismo de reflexión y se devuelve el valor de retorno del método. Se puede realizar lógica adicional antes y después de llamar a los métodos del objeto de destino.

Código de implementación común

public class JDKProxyFactory implements InvocationHandler {
    
    

    //需要被代理的对象
    private Object object;

    public JDKProxyFactory(Object object) {
    
    
        this.object = object;
    }

    @SuppressWarnings("unchecked")
    public <T> T getProxy(){
    
    
        return (T) Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),//当前线程的上下文ClassLoader
                object.getClass().getInterfaces(), //代理需要实现的接口
                this); // 处理器自身
    }
	
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        Object result = null;
		//进行方法匹配,调用对应方法名的方法
        if ("loadVideo".equals(method.getName())) {
    
    
            result=method.invoke(object, args);
        }

        if ("playVideo".equals(method.getName())) {
    
    
            System.out.println("前置增强");
            result=method.invoke(object, args);
            System.out.println("后置增强");
        }
        return result;
    }
}

llamada del cliente

public class Client2 {
    
    
    public static void main(String[] args) {
    
    
        Player player=new VPlayer();
        Player proxy=new JDKProxyFactory(player).getProxy();
        proxy.loadVideo("aaa.mp4");
        proxy.playVideo("aaa.mp4");

/*      或者
        Player p=new VPlayer();
        Player o = (Player) Proxy.newProxyInstance(
                p.getClass().getClassLoader(),
                p.getClass().getInterfaces(),
                new VPlayerProxyFactory(p)
        );
        o.loadVideo("aaaa.mp4");
*/
    }
}

imagen-20230610200031639

4 implementación de proxy dinámico CGlib

CGLIB (Biblioteca de generación de código) es una biblioteca de generación de código basada en ASM (Java Bytecode Operation Framework) que puede generar dinámicamente una subclase de la clase de destino como una clase proxy en tiempo de ejecución y anular los métodos que contiene para implementar la función de proxy. A diferencia del proxy dinámico JDK que viene con Java, el proxy dinámico CGlib puede representar clases que no implementan interfaces. El principio se divide en los siguientes pasos:

  1. Crear objeto Enhancer: Enhancer es la clase principal de la biblioteca CGLIB que se utiliza para generar subclases dinámicamente. Se puede generar una clase de proxy creando un objeto Enhancer y configurando parámetros como la clase de destino y el interceptor que requieren proxy.
  2. Establecer interceptor de devolución de llamada: al generar la clase de proxy, debe especificar el interceptor. El interceptor es la clave para implementar la lógica del proxy: intercepta la llamada cuando se llama al método de la clase proxy y ejecuta la lógica correspondiente. En CGLIB, el interceptor necesita implementar la interfaz MethodInterceptor.
  3. Cree un objeto proxy: al llamar al método de creación del objeto Enhancer, puede generar un objeto proxy. El objeto proxy heredará los métodos de la clase de destino y, al llamar al método del objeto proxy, primero se llamará al método de intercepción del interceptor y luego se ejecutará el método de destino.
  4. Llamar al objeto proxy: al llamar al método del objeto proxy, se activará el método de intercepción del interceptor. En el método de intercepción, se pueden ejecutar diversas lógicas según sea necesario, como agregar registros, estadísticas de rendimiento, gestión de transacciones, etc.

Definir un APlayer de clase real independiente sin interfaz

//音频播放器
public class APlayer {
    
    
    public void loadAudio(String filename) {
    
    
        System.out.println("加载MP3音频文件:"+filename);
    }

    public void playAudio(String filename) {
    
    
        System.out.println("播放MP3:"+filename);
    }
}

Código de implementación común

public class CglibProxyFactory implements MethodInterceptor {
    
    

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
    
    
        Enhancer en = new Enhancer();
        //设置代理的父类
        en.setSuperclass(clazz);
        //设置方法回调
        en.setCallback(this);
        //创建代理实例
        return (T)en.create();
    }

    @Override
    //参数中的object是目标对象,method和args是目标对象的方法和参数,methodProxy是方法代理
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    
    
        Object result = null;

        if ("loadAudio".equals(method.getName())) {
    
    
            //通过继承的方法实现代理,因此这里调用invokeSuper
            result = methodProxy.invokeSuper(object, args);
        }
        if ("playAudio".equals(method.getName())) {
    
    
            result = methodProxy.invokeSuper(object, args);
        }
        return result;
    }
}

llamada del cliente

public class Client3 {
    
    
    public static void main(String[] args) {
    
    
        APlayer aplayer=new APlayer();
        APlayer proxy = new CglibProxyFactory().getProxy(aplayer.getClass());
        //验证代理类的父类
        System.out.println("代理类的父类:"+proxy.getClass().getSuperclass().getSimpleName());
        System.out.println();

        proxy.loadAudio("荷塘月色.mp3");
        proxy.playAudio("荷塘月色.mp3");
    }
}

imagen-20230610201932707

5 Resumen

El modo proxy puede implementar funciones como control de acceso y optimización del rendimiento para clases reales sin modificar el código de clase real. Hay dos formas de implementar el patrón de proxy en Java: proxy estático y proxy dinámico. Los proxies estáticos requieren escribir clases de proxy manualmente antes de la compilación, mientras que los proxies dinámicos pueden generar clases de proxy dinámicamente en tiempo de ejecución.

Los proxies dinámicos se dividen en proxies dinámicos JDK y proxies dinámicos CGlib. Generalmente usamos el primero, cuando la clase de proxy no tiene interfaz, el segundo se usa como una solución complementaria al primero. Las ventajas del patrón proxy incluyen reducir el acoplamiento del sistema, mejorar la escalabilidad del código, mejorar la seguridad del sistema y simplificar las interfaces, pero también aumenta la complejidad del sistema y puede afectar el rendimiento. El modo proxy es una buena opción en escenarios donde se requiere control de acceso, proxy remoto y mejora de funciones.

Supongo que te gusta

Origin blog.csdn.net/ZGL_cyy/article/details/132910893
Recomendado
Clasificación