[Java esencial] Modo proxy de Java (proxy estático, proxy dinámico JDK / Cglib)

Introducción

Creo que como desarrollador de Java, deberías haber usado o visto el patrón de diseño de proxy. Como Spring's AOP, es el modo de proxy dinámico usado. La interfaz xxxMapper en Mybatis es también el proxy dinámico usado para generar el correspondiente objeto de proxy Mapper. La importancia del modelo de agente dinámico.

El modo de proxy generalmente se divide en proxy estático y proxy dinámico, el objetivo es expandir y mejorar la función del método original.

Proxy estático

  • Rol de objeto abstracto (AbstractObject): generalmente, las interfaces o clases abstractas se utilizan para declarar la interfaz común entre el objeto de destino y el objeto de proxy, de modo que el objeto de proxy se pueda utilizar donde sea que se pueda utilizar el objeto de destino.
  • Rol de objeto de destino (RealObject): el rol real que se está representando.
  • Rol ProxyObject (ProxyObject): Implementa la misma interfaz del objeto de destino, contiene una referencia al objeto de destino y puede personalizar y expandir el método de destino.

Definir una interfaz de pedido y agregar un método de pedido

public interface IOrderService {
    // 提交订单
    void submitOrder();
}

Clase de implementación de interfaz de orden de escritura

public class OrderServiceImpl implements IOrderService{

    // 提交订单测试
    @Override
    public void submitOrder() {
        System.out.println("-------保存订单-------");
    }
}

Luego, si el método submitOrder () actual ya no puede satisfacer nuestras necesidades, necesitamos realizar un procesamiento comercial especial antes y después de este método, pero no queremos modificar la clase de implementación de la interfaz de pedido original (principio de apertura y cierre), por lo que Puedes agregar una clase de proxy de orden. Esta clase de proxy define los atributos de miembro de un objeto de interfaz de destino. Mediante la inyección de construcción, puedes manipular el objeto de destino. Puedes agregar lógica personalizada antes y después del método del objeto de destino:

public class OrderStaticProxy implements IOrderService {

    private IOrderService orderService;

    // 构造注入
    public OrderStaticProxy(IOrderService orderService){
        this.orderService=orderService;
    }

    @Override
    public void submitOrder() {
        System.out.println("--------提交订单前,自定义逻辑");
        orderService.submitOrder();
        System.out.println("--------提交订单前,自定义逻辑");
    }
}

Categoría de prueba:

public class Test {

    public static void main(String[] args) {
        IOrderService orderService = new OrderServiceImpl();
        OrderStaticProxy proxy = new OrderStaticProxy(orderService);
        proxy.submitOrder();
    }
}

El resultado es el siguiente:

Análisis: sin cambiar la clase de implementación OrderServiceImpl original, la función de la interfaz de pedido original se amplía y mejora

ventaja:

  • Puede expandir la función del objeto de destino sin modificar el objeto de destino;
  • Los servicios públicos son manejados por agentes, lo que es más conveniente;
  • Se ajusta al principio de apertura y cierre y al principio de unicidad.

Desventajas:

  • La clase de proxy necesita implementar la misma interfaz que el objeto de destino, por lo que cuando cada clase necesita escribir una clase de proxy, provoca demasiadas clases y un mantenimiento inconveniente;
  • Si se agregan métodos a la interfaz, tanto la clase de implementación de destino como la clase de proxy deben modificarse.

Por tanto, conducimos a agentes dinámicos.

Proxy dinámico

El papel de los agentes dinámicos es el mismo que el de los agentes estáticos.

El proxy estático proporcionará una clase de proxy para cada mejora comercial y la clase de proxy creará objetos de proxy; mientras que el proxy dinámico no tiene una clase de proxy, los objetos de proxy se generan directamente de forma dinámica mediante la herramienta de generación de proxy.

El objeto de proxy del proxy dinámico se genera dinámicamente en tiempo de ejecución. La clase de proxy del proxy estático debe escribirse de antemano, es decir, la clase de proxy se ha determinado en tiempo de compilación.

Los agentes dinámicos se dividen en:

  • Proxy dinámico basado en interfaz (proxy dinámico JDK)
  • Proxy dinámico basado en clases (cglib)

Proxy dinámico JDK

En el proxy dinámico de JDK, el núcleo es la interfaz InvocationHandler y la clase Proxy . Para obtener más información, consulte la documentación de ayuda de JDK.

package java.lang.reflect;

public interface InvocationHandler {

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

Entre ellos, necesitamos implementar el método InvocationHandler # invoke () , los parámetros son:

  • Proxy de objeto: objeto de proxy (básicamente inútil)
  • Método método: el método llamado por el objeto proxy
  • Object [] args: parámetros en el método llamado

Genere objetos de clase de proxy a través de Proxy # newProxyInstance ()

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //。。。。
    }

Entre ellos, el método Proxyes un método estático en la clase y los parámetros son:

  • ClassLoader loader: Especifique el objeto de destino actual para usar el cargador de clases, y el método para obtener el cargador es fijo.
  • Class<?>[] interfaces: El tipo de interfaz implementada por el objeto de destino, y el tipo se confirma mediante un método genérico.
  • InvocationHandler h: Controlador de eventos, cuando se ejecuta el método del objeto de destino, se activará el método invoke () del controlador de eventos y el método del objeto de destino actualmente ejecutado se pasará como parámetro.

Código

La función del objeto abstracto y la función del objeto de destino son las mismas que las del proxy estático (IOrderService -> OrderServiceImpl).

Personalice la implementación de InvocationHandler # invoke () de la siguiente manera

public class DynamicProxy implements InvocationHandler {


    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("------动态代理,前置增强-----");
        Object invoke = method.invoke(target, args);
        System.out.println("------动态代理,后置增强-----");
        return invoke;
    }
    // 生成动态代理对象
    public static Object getProxy(Object target){
        DynamicProxy proxy = new DynamicProxy(target);
        return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), target.getClass().getInterfaces(),proxy);
    }

}

Categoría de prueba:

/**
 * @description: 测试
 * @author: stwen_gan
 * @date: 
 **/
public class Test {

    public static void main(String[] args) {

        // 为了在项目下生成代理类
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        
        // 静态代理
//        IOrderService orderService = new OrderServiceImpl();
//        OrderStaticProxy proxy = new OrderStaticProxy(orderService);
//        proxy.submitOrder();

        // 动态代理
        IOrderService orderService = new OrderServiceImpl();
        IOrderService dynamicProxy = (IOrderService) DynamicProxy.getProxy(orderService);
        dynamicProxy.submitOrder();


    }
}

Salida:

Entre ellos, agregue la siguiente declaración de configuración

// Esta configuración se usa para generar la clase generada por el proxy dinámico cg1ib

// System.setProperty (Debuggingclasswriter.DEBUG_LOCATION_PROPERTY, "D: \\ class");

// Esta configuración se usa para generar la clase generada por el proxy dinámico jdk

System.getProperties (). Put ("sun.misc.ProxyGenerator.saveGeneratedFiles", "verdadero");

Estará en nuestro directorio actual, el JDK generará una clase de proxy por defecto, el nombre por defecto es $ Proxy0 , que hereda la clase Proxy

Núcleo : un agente dinámico, que generalmente actúa para un cierto tipo de negocio, puede actuar para varias clases y actuar sobre interfaces.

Como puede verse en el código anterior, el objeto de proxy dinámico no necesita implementar la interfaz del objeto de destino, pero el objeto de destino debe implementar la interfaz; de lo contrario, no se puede utilizar el proxy dinámico de JDK.

Mejora : el tipo de objeto de retorno es genérico y la interfaz abstracta no puede tener una clase de implementación. Similar a la interfaz xxxMapper en Mybatis, la capa inferior también es para generar objetos de proxy de mapeador a través de un proxy dinámico para empalmar y ejecutar SQL. No necesitamos escribir clases de implementación de mapeador.

Proxy cglib

El proxy estático anterior y los modos de proxy dinámico JDK requieren que el objeto de destino implemente una interfaz, pero a veces, el objeto de destino puede ser solo una clase independiente sin implementar ninguna interfaz. En este momento, podemos usar la subclase de objeto de destino La forma de realizar el proxy, este método de proxy es: proxy Cglib, relativamente menos utilizado. ( Cita )

El principio del proxy dinámico de Cglib es generar una subclase de la clase de destino (es decir, el objeto de subclase es un objeto de proxy), que construye un objeto de subclase en la memoria para realizar la expansión de la función del objeto de destino .

Nota: El proxy dinámico de Cglib se puede usar independientemente de si la interfaz está implementada o no, no solo cuando no hay una interfaz.

  • El proxy dinámico de JDK tiene una limitación, es decir, el objeto de destino que usa el proxy dinámico debe implementar al menos una interfaz. Por lo tanto, si el objeto de destino que no implementa la interfaz pero quiere usar el proxy, puede usar el proxy Cglib.
  • Cglib es un potente paquete de generación de código de alto rendimiento que puede expandir las clases Java e implementar interfaces Java durante el tiempo de ejecución. Es ampliamente utilizado por muchos marcos AOP, como Spring AOP y synaop, para proporcionarles métodos interception(intercepción).
  • La capa inferior del paquete Cglib es para convertir el código de bytes y generar nuevas clases mediante el uso de un pequeño y rápido bytecode ASM marco procesamiento. El uso directo de ASM no se recomienda porque requiere comprender la estructura interna de la JVM, incluyendo el formato del archivo de clase. Familiarizado con el conjunto de instrucciones.

Método Interceptor

public interface MethodInterceptor extends Callback {
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}

Clase de herramienta Cglib, solo necesitamos implementar MethodInterceptor # intercept () para mejorar el método de destino

public class CglibProxy implements MethodInterceptor {

    //维护目标对象
    private Object target;
    public CglibProxy(Object target) {
        this.target = target;
    }

//    //获取目标对象的代理对象
//    public Object getProxy() {
//        //1. 实例化工具类
//        Enhancer en = new Enhancer();
//        //2. 设置父类对象
//        en.setSuperclass(this.target.getClass());
//        //3. 设置回调函数
//        en.setCallback(this);
//        //4. 创建子类,也就是代理对象
//        return en.create();
//    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("-------动态代理,前置增强-----");
        //执行目标对象的方法
        Object object = method.invoke(target, objects);
        System.out.println("-------动态代理,后置增强-----");
        return object;
    }
}

Entre ellos, el método comentado es obtener el objeto proxy de la clase de destino. Podemos extraerlo por separado y agregar una fábrica de proxy cglib:

/**
 * @description: cglib代理工厂
 * @author: stwen_gan
 * @date: 
 **/
public class CglibProxyFactory {
    //获取目标类的代理对象
    public static <T> T createProxy(final Class<?> targetClass, final MethodInterceptor methodInterceptor) {
        return (T) Enhancer.create(targetClass,methodInterceptor);
    }
}

Agregue una nueva clase OrderDao, sin necesidad de implementar ninguna interfaz

public class OrderDao {
    
    void submitOrder(){
        System.out.println("--------保存订单-------");
    }
}

Categoría de prueba:

Salida:

para resumir

En comparación con los agentes estáticos, los agentes dinámicos JDK son similares en el sentido de que todos son programación orientada a la interfaz, lo que mejora la función del método de destino sin modificar el código original, y los agentes dinámicos no necesitan escribir clases de agente correspondientes uno a uno para objetos de destino como agentes estáticos. El uso del modelo de agente se ajusta al "principio de apertura y cierre", y la división de responsabilidades funcionales es clara, el objetivo es ampliar y potenciar las funciones del método original, facilitando su ampliación y mantenimiento posteriormente.

● La optimización de rendimiento de Tomcat8 más sólida de la historia

¿Por qué Alibaba puede resistir 10 mil millones en 90 segundos? - La evolución de la arquitectura distribuida de alta concurrencia del lado del servidor

Plataforma de comercio electrónico B2B: función de pago electrónico ChinaPay UnionPay

Aprenda el candado distribuido de Zookeeper, deje que los entrevistadores lo miren con admiración

Solución de bloqueo distribuido de Redisson con microservicio de pico de comercio electrónico de SpringCloud

Vea más artículos buenos, ingrese a la cuenta oficial, por favor, excelente en el pasado

Una cuenta pública profunda y conmovedora 0.0

Supongo que te gusta

Origin blog.csdn.net/a1036645146/article/details/111881599
Recomendado
Clasificación