¡Dos años de desarrollo de Java! Incluso si no sabe cómo usar el proxy dinámico JDK, ¡no tiene esperanzas de aumentar su salario!

Proxy dinámico JDK

Para presentar la agencia dinámica, ¡echemos un vistazo a un caso!

Guangzhou, Guangdong, a las 9:00 de la mañana, un niño bonito entró en la tienda de té de la mañana con chanclas y una jaula para pájaros. Así es, ¡esta es una empresa de vuelos chárter típica de Guangzhou! Algunos edificios a su nombre, solo se alquilan para ganarse la vida, no hay trabajo , ¡esta persona es realmente aburrida!

Aquí surge la pregunta: ¿El cobro del alquiler cuenta como trabajo? Bueno, de hecho, los verdaderos fletadores no cobran el alquiler por su cuenta , están confiados a intermediarios para hacerlo. ¿por qué? Se puede decir que esto implica seguridad, privacidad, etc. Piénselo, si el fletador cobra el alquiler por sí mismo, hay muchos inquilinos en este momento y otros fletadores están molestos. Simplemente le piden a alguien que cause problemas, como preguntar si alquila o perder el tiempo para el fletador. Por supuesto que no solo eso ...

Fácil de usar

Está bien, el agente de alquiler saldrá y el inquilino mirará la casa, negociará el precio, firmará el contrato, entregará las llaves, etc., y dejará que el agente (agente) lo haga, y el propietario simplemente esperará el dinero.

¡Dos años de desarrollo de Java!  Incluso si no sabe cómo usar el proxy dinámico JDK, ¡no tiene esperanzas de aumentar su salario!

Utilice declaraciones de salida en el código en lugar de negocios reales.

Primero, definimos una interfaz de propietario y una clase de implementación de propietario . (¿Por qué necesita una interfaz? La clase de proxy se obtiene en función de la interfaz, y la clase de propietario no debe exponerse)

public interface ILandlordService {
    void deliver();
}
public class LandlordServiceImpl implements ILandlordService {
    public void deliver() {
        try {
            System.out.println("告知房东出租成功,房东收钱");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("出现异常,终止");
        }
    }
}

A continuación, cree una clase para implementar la interfaz java.lang.reflect.InvocationHandler , que se utiliza para mejorar el método original. (Preste atención al paquete, está engañado)

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

public class TransactionInvocationHandler implements InvocationHandler {
    // 需要被代理的对象(房东)
    private Object target;

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    /*
        实现接口需要重写的方法
        proxy:代理对象
        method:房东接口中被调用的方法
        args:房东接口中被调用的方法需要的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object retVal = null;
        try {
            System.out.println("交付前与中介的交谈");
            // 房东收钱
            retVal = method.invoke(target, args);
            System.out.println("租房成功");
            System.out.println("给钥匙");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("出现异常,终止交付");
        }
        return retVal;
    }
}

Finalmente escriba la clase de prueba (inquilino)

import org.junit.Test;
import java.lang.reflect.Proxy;

public class TenantTest {

    @Test
    public void test() {

        //这两个创建对象和设置值应在spring中配置,让spring创建、设值
        // 创建一个房东对象
        ILandlordService landlordService = new LandlordServiceImpl();
        // 创建增强类对象
        TransactionInvocationHandler transactionInvocationHandler = new TransactionInvocationHandler();
        // 把房东对象设置到增强类对象中使用
        transactionInvocationHandler.setTarget(landlordService);

        /*
             使用强转得到房东类代理对象(中介)
             newProxyInstance方法需要三个参数
             ClassLoader loader:类加载器,直接使用本类的类加载器(系统类加载器)
             Class<?>[] interfaces:接口数组
             InvocationHandler h:增强类
         */
        ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                // 这里landlordService对象暂时在测试类中创建,
                // 使用spring后,注入到TransactionInvocationHandler(增强类)的字段(target)中,
                // 直接到增强类中获取,这样测试类(租客)就不知道实现类了(房东)
                landlordService.getClass().getInterfaces(),
                transactionInvocationHandler
        );
        // 调用代理对象的deliver(),会执行增强类的invoke方法,参数method为代理对象调用的方法
        proxy.deliver();
    }
}
执行结果:
交付前与中介的交谈
告知房东出租成功,房东收钱
租房成功
给钥匙

En este momento, mucha gente está desconcertada, ¿cómo crear una clase proxy? ¿Cómo se puede llamar al método de invocación de la clase mejorada? ¡Entonces veamos el principio! Se recomienda mirar la captura de pantalla, ¡el código es demasiado!

principio

Pruebe el método newProxyInstance en la clase, haga clic en él

ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
    this.getClass().getClassLoader(),
    landlordService.getClass().getInterfaces(),
    transactionInvocationHandler
);

Vaya al método newProxyInstance de la clase de proxy para guardar el código innecesario

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) {
    /*
    * Look up or generate the designated proxy class and its constructor.
    * 查找或生成 指定的代理类和它的构造函数
    */
    /*
        获取生成类的构造器
        loader:类加载器
        interfaces:接口数组
    */
    Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
    return newProxyInstance(caller, cons, h);
}

Vaya al método newProxyInstance para guardar el código innecesario e intente atrapar

private static Object newProxyInstance(Class<?> caller,
                                           Constructor<?> cons,
                                           InvocationHandler h) {
    // caller有关安全的,这里忽略
    checkNewProxyPermission(caller, cons.getDeclaringClass());
     /* 
        反射调用构造器创建这个类的对象
        cons:代理类构造器
        h:增强类
    */
    return cons.newInstance(new Object[]{h});
}

Utilice el siguiente código para obtener el archivo de código de bytes de la clase de proxy

ProxyGenerator ya no es público en JDK 11. El código fuente que usé aquí es JKD 11 para
obtener el código de bytes de la clase de proxy usando JDK 8.

import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;

public class DynamicProxyClassGenerator {
    public static void main(String[] args) throws Exception {
        // 实现类的class对象
        generateClassFile(LandlordServiceImpl.class, "LandlordServiceProxy");
    }

    public static void generateClassFile(Class<?> targetClass, String proxyName) throws Exception {
        // 根据类信息和提供的代理类名称,生成字节码
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, targetClass.getInterfaces());
        String path = targetClass.getResource(".").getPath();
        System.out.println(path);
        FileOutputStream out = null;
        // 保留到硬盘中
        out = new FileOutputStream(path + proxyName + ".class");
        out.write(classFile);
        out.close();
    }
}

El archivo de clase generado por la clase proxy es así, el método deliver () se basa en la clase que definimos, omitiendo el contenido del método obtenido por la clase Object

Llame al constructor parametrizado para crear un objeto

import com.hao.service.ILandlordService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class LandlordServiceProxy extends Proxy implements ILandlordService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

     // 创建对象时调用的方法
    public LandlordServiceProxy(InvocationHandler var1) throws  {
        // 调用父类(proxy类)构造器
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
    }

    public final String toString() throws  {
    }

    public final void deliver() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.hao.service.ILandlordService").getMethod("deliver");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    // 把增强类设置到proxy类的字段上
    this.h = h;
}
protected InvocationHandler h;

Finalmente Proxy.newProxyInstance () devuelve la clase de proxy (intermediario)

ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
);
proxy.deliver();

Luego llamamos al método deliver () e ingresamos el método deliver () de la clase de proxy

public final void deliver() throws  {
    try {
        // 调用父类的h字段(增强类)中的invoke方法,m3在本类static{}中获取
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

m3 está en estático {}

static {
    try {
        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        m2 = Class.forName("java.lang.Object").getMethod("toString");
        // 通过反射得到deliver方法
        m3 = Class.forName("com.hao.service.ILandlordService").getMethod("deliver");
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    } catch (NoSuchMethodException var2) {
        throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
        throw new NoClassDefFoundError(var3.getMessage());
    }
}

Al final

Se puede ver que llamar a la clase de proxy eventualmente llamará al método de invocación de la clase mejorada. Gracias por ver este artículo. Corrígeme si hay alguna deficiencia en el artículo. Si crees que el artículo es útil para ti, recuerda darme el visto bueno. Compartirás artículos técnicos relacionados con Java o información de la industria todos los días. Bienvenidos a todos a seguir y reenviar el artículo

Supongo que te gusta

Origin blog.51cto.com/14966465/2542738
Recomendado
Clasificación