El uso del modo proxy

1. El uso del modo agente

1.1 Introducción

Por alguna razón, es necesario proporcionar un objeto con un proxy para controlar el acceso a ese objeto . En este momento, el objeto de acceso no es adecuado o no puede referirse directamente al objeto de destino, y el objeto proxy actúa como intermediario entre el objeto de acceso y el objeto de destino .

Los proxies en Java se dividen en proxies estáticos y proxies dinámicos según el tiempo de generación de las clases de proxy . La clase de proxy estático se 编译期genera en Java, mientras que la clase de proxy dinámico se genera en Java 运行时动态生成. Hay dos tipos de proxy dinámico: proxy JDK y proxy CGLib .

1.2 Estructura

  • Clase de sujeto abstracto (Subject) : declara los métodos comerciales implementados por sujetos reales y objetos proxy a través de interfaces o clases abstractas.
  • Clase de sujeto real (Real Subject) : realiza el negocio específico en el sujeto abstracto, es el objeto real representado por el objeto proxy y es el objeto final al que se hace referencia.
  • Clase Proxy (Proxy) : proporciona la misma interfaz que el tema real, que contiene referencias al tema real, que puede acceder, controlar o ampliar las funciones del tema real.

1.3 Proxy estático

1.3.1 Diagrama de clases de casos de compra de billetes de estación de tren

Si desea comprar un boleto de tren, debe ir a la estación de tren para comprar el boleto, tomar el tren hasta la estación de tren, esperar en la fila y esperar una serie de operaciones, lo que obviamente es más problemático. Y la estación de tren tiene oficinas de venta en muchos lugares, por lo que es mucho más conveniente para nosotros ir a las oficinas de venta para comprar boletos. Este ejemplo es en realidad un modelo de agente típico, la estación de tren es el objeto de destino y la agencia de ventas es el objeto de agente.

inserte la descripción de la imagen aquí

1.3.2 Código

El acceso directo es al objeto de clase ProxyPoint, es decir, el ProxyPoint actúa como intermediario entre el objeto de acceso y el objeto de destino. Al mismo tiempo, también se ha mejorado el método de venta (los agentes cobran algunas tarifas de servicio).

/**
 * 卖火车票的接口
 */
public interface SellTickets {
    
    
    void sell();
}

/**
 * 火车站类
 */
public class TrainStation implements SellTickets {
    
    
    public void sell() {
    
    
        System.out.println("火车站卖票");
    }
}

/**
 * 代售点类
 */
public class ProxyPoint implements SellTickets {
    
    
    //声明火车站类对象
    private TrainStation trainStation  = new TrainStation();
    public void sell() {
    
    
        System.out.println("代售点收取一些服务费用");
        trainStation.sell();
    }
}

/**
 * 客户:Client
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        //创建代售点类对象
        ProxyPoint proxyPoint = new ProxyPoint();
        //调用方法进行买票
        proxyPoint.sell();
    }
}

1.4 Proxy dinámico JDK

Utilice el agente dinámico proporcionado por JDK para realizar el caso de comprar boletos en la estación de tren.

Java proporciona una clase proxy dinámica Proxy Proxy no es la clase del objeto proxy que mencionamos anteriormente, pero proporciona un método estático ( newProxyInstancemétodo) para crear un objeto proxy para obtener el objeto proxy .

1.4.1 Código

ProxyFactoryNo es la clase de proxy mencionada en el modo de proxy, pero la clase de proxy es una clase generada dinámicamente en la memoria durante la ejecución del programa .

/**
 * 卖火车票的接口
 */
public interface SellTickets {
    
    
    void sell();
}

/**
 * 火车站类
 */
public class TrainStation implements SellTickets {
    
    
    public void sell() {
    
    
        System.out.println("火车站卖票");
    }
}

/**
 * 获取代理对象的工厂类
 * 代理类也实现了对应的接口
 */
public class ProxyFactory {
    
    
    // 声明目标对象
    private TrainStation station = new TrainStation();
    // 获取代理对象的方法,使用Proxy获取代理对象
    public SellTickets getProxyObject() {
    
    
        /*
            newProxyInstance()方法参数说明:
                ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器
                Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
                InvocationHandler h : 代理对象的调用处理程序
         */
        SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(
                station.getClass().getClassLoader(),
                station.getClass().getInterfaces(),
                new InvocationHandler() {
    
    
                    /*
                        InvocationHandler中invoke方法参数说明:
                            proxy : 代理对象
                            method : 对应于在代理对象上调用的接口方法的 Method 实例
                            args : 代理对象调用接口方法时传递的实际参数
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                        System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
                        //执行真实对象
                        Object result = method.invoke(station, args);
                        return result;
                    }
                });
        return sellTickets;
    }
}

/**
 * 客户: Client
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        // 获取代理对象,创建代理工厂对象
        ProxyFactory factory = new ProxyFactory();
        // 使用factory对象的方法获取代理对象
        SellTickets proxyObject = factory.getProxyObject();
        // 调用卖火车票的方法
        proxyObject.sell();
    }
}

1.4.2 Proceso de ejecución del agente dinámico JDK

  • Llame al método a través del objeto proxy en la clase de prueba sell().
    • De acuerdo con las características del polimorfismo, sell()se ejecuta el método de la clase proxy.
    • El método sell() en la clase de proxy llama al método InvocationHandlerdel objeto de clase de subimplementación de la interfaz invoke.
    • invokeEl método ejecuta (TrainStation)中的sell()el método de la clase a la que pertenece el objeto real a través de la reflexión.

1.5 Proxy dinámico CGLIB

Utilice el proxy CGLIB para realizar el caso de comprar boletos en la estación de tren.

Si no se define la interfaz SellTickets , solo se define TrainStation (clase de estación de tren). Obviamente, el proxy JDK no se puede usar, porque el proxy dinámico JDK requiere que la interfaz se defina para representar la interfaz.

CGLIB es un potente paquete de generación de código de alto rendimiento. Proporciona proxies para clases que no implementan interfaces y proporciona un buen complemento para los proxies dinámicos de JDK.

1.5.1 Paquete de importación

Para construir un proyecto Maven, debe introducir las coordenadas del paquete jar, porque CGLIB es un paquete proporcionado por un tercero.

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

1.5.2 Código

/**
 * 火车站类
 */
public class TrainStation {
    
    
    public void sell() {
    
    
        System.out.println("火车站卖票");
    }
}

/**
 * 代理对象工厂,用来获取代理对象
 */
public class ProxyFactory implements MethodInterceptor {
    
    
    // 声明火车站对象
    private TrainStation station = new TrainStation();
    public TrainStation getProxyObject() {
    
    
        // 创建Enhancer对象,类似于JDK代理中的Proxy类
        Enhancer enhancer = new Enhancer();
        // 设置父类的字节码对象,指定TrainStation为父类
        enhancer.setSuperclass(TrainStation.class);
        // 设置回调函数
        enhancer.setCallback(this);
        // 创建代理对象
        TrainStation proxyObject = (TrainStation) enhancer.create();
        return proxyObject;
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    
        System.out.println("代售点收取一定的服务费用(CGLib代理)");
        // 要调用目标对象的方法
        Object obj = method.invoke(station, objects);
        return obj;
    }
}

/**
 * 客户: Client
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        // 创建代理工厂对象
        ProxyFactory factory = new ProxyFactory();
        // 获取代理对象
        TrainStation proxyObject = factory.getProxyObject();
        // 调用代理对象中的sell方法卖票
        proxyObject.sell();
    }
}

1.6 Comparación de tres agentes

  • jdk代理yCGLIB代理

    Use CGLib para implementar proxy dinámico. La capa inferior de CGLib adopta el marco de generación de código de bytes ASM, y la tecnología de código de bytes se usa para generar clases de proxy. Antes de JDK1.6, es más eficiente que usar la reflexión de Java. Lo único que se debe tener en cuenta es que CGLib no puede representar una clase o método declarado como final , porque el principio de CGLib es generar dinámicamente subclases de la clase representada.

    Después de que JDK1.6, JDK1.7 y JDK1.8 optimizaron gradualmente el proxy dinámico JDK, la eficiencia del proxy JDK es mayor que la del proxy CGLib cuando la cantidad de llamadas es pequeña. Solo cuando se realiza una gran cantidad de llamadas, JDK1 .6 y JDK1.7 es un poco menos eficiente que el proxy CGLib, pero cuando se trata de JDK1.8, el proxy JDK es más eficiente que el proxy CGLib . Entonces, si hay una interfaz, use el proxy dinámico JDK, si no hay una interfaz, use el proxy CGLIB .

  • 动态代理y静态代理

    En comparación con el proxy estático, la mayor ventaja del proxy dinámico es que todos los métodos declarados en la interfaz se transfieren a un método centralizado del procesador de llamadas para su procesamiento ( InvocationHandler.invoke). De esta forma, cuando hay una gran cantidad de métodos de interfaz, podemos manejarlos de manera flexible, sin necesidad de transferir cada método como un proxy estático.

    Si la interfaz agrega un método, además de todas las clases de implementación que necesitan implementar este método en el modo de proxy estático, todas las clases de proxy también deben implementar este método. Aumento de la complejidad del mantenimiento del código. Este problema no ocurre con proxies dinámicos

1.7 Ventajas y desventajas

ventaja:

  • El modo proxy desempeña un papel de intermediario entre el cliente y el objeto de destino y protege el objeto de destino;
  • El objeto proxy puede ampliar la funcionalidad del objeto de destino;
  • El modo proxy puede separar al cliente del objeto de destino, reduciendo el acoplamiento del sistema hasta cierto punto;

defecto:

  • Mayor complejidad del sistema;

1.8 Escenarios de uso

  • 远程(Remote)代理

    Los servicios locales solicitan servicios remotos a través de la red . Para lograr la comunicación de local a remoto, necesitamos implementar la comunicación de red y manejar las posibles excepciones. Para un buen diseño de código y mantenibilidad, ocultamos la parte de comunicación de la red y solo exponemos una interfaz al servicio local , a través de la cual se puede acceder a las funciones proporcionadas por el servicio remoto sin prestar demasiada atención a los detalles de la parte de comunicación.

  • 防火墙(Firewall)代理

    Cuando configura su navegador para usar la función de proxy, el firewall reenvía la solicitud de su navegador a Internet; cuando Internet devuelve una respuesta, el servidor proxy la reenvía a su navegador.

  • 保护(Protect or Access)代理

    Controle el acceso a un objeto y, si lo desea, proporcione diferentes niveles de acceso a diferentes usuarios .

记录每一个学习瞬间

Supongo que te gusta

Origin blog.csdn.net/qq_51601665/article/details/131118784
Recomendado
Clasificación