El principio subyacente del proxy dinámico de Java

1. Modelo de agencia

代理模式Es uno de los patrones de diseño más utilizados, su característica esLa clase de proxy y la clase de proxy tienen la misma interfaz. La clase de proxy puede realizar un procesamiento previo y posterior para la ejecución del método de clase de proxy para mejorar el método de clase de proxy

Inserte la descripción de la imagen aquí

La estructura de clases del modelo de proxy suele ser como se muestra en la figura anterior. Habrá una relación de asociación entre la clase de proxy y la clase de proxy. Un objeto de la clase de proxy contiene un objeto de la clase de proxy. El objeto de la clase de proxy en sí no implementa realmente el servicio, pero proporciona servicios específicos llamando a los métodos relevantes del objeto de la clase de proxy.

2. Uso de proxy dinámico

La clase de proxy no está definida en el código Java, pero se genera dinámicamente en tiempo de ejecución de acuerdo con las "instrucciones" en el código Java ( 字节码由JVM在运行时动态生成而非预存在任何一个 .class 文件中),Este método de proxy para crear clases de proxy mientras se ejecuta el programa se llama动态代理Su ventaja es que puede procesar cómodamente las funciones de la clase de proxy. Esto se debe a que todos los métodos ejecutados por el proxy se InvocationHandler#invoke()llaman a través de métodos, lo que equivale a poner un shell en todos los métodos de la clase de proxy, por lo que siempre que este método se procese de manera uniforme, todos los métodos de proxy pueden ser iguales. Operado

El siguiente código muestra el uso simple de proxy dinámico, los pasos básicos son los siguientes:

  1. Definir una interfaz pública, en este caso IHello, hay un método abstracto en la interfaz.
  2. La clase de entidad define implementa una interfaz común como la clase de proxy, la presente realización es la clase de proxy Helloimplementa IHellola interfaz, el método abstracto reemplaza la interfaz
  3. Define implementa una InvocationHandlerinterfaz de clase de interceptación de métodos, reescribiendo el invoke()método para implementar la clase de proxy de interceptación lógica de procesamiento cuando se ejecuta el método de
  4. Al Proxy.newProxyInstance()generar un método de objeto proxy, la implementación de un método de interfaz después de que el objeto proxy pueda contener
public class ServiceProxy {
    
    

    public interface IHello {
    
    
        String sayHi();
    }

    public static class Hello implements IHello {
    
    
        @Override
        public String sayHi() {
    
    
            return "Hello";
        }
    }

    // 动态代理类
    public static class ProxyHandler<T> implements InvocationHandler {
    
    
        private T origin;

        public ProxyHandler(T origin) {
    
    
            this.origin = origin;
        }

       /**
         * @param o 代理对象引用
         * @param method 正在执行目标的方法
         * @param objects 目标方法执行时的入参
        */
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
    
    
            String s = "proxy";
            s += method.invoke(origin, objects);
            return s;
        }
    }

    public static void main(String[] args) {
    
    
        IHello IHello = (IHello) getInstance(IHello.class, new ProxyHandler<>(new Hello()));

        System.out.println(IHello.toString());

        generateProxyClass();
    }

    // 创建代理对象
    public static <T> Object getInstance(Class<T> clazz, ProxyHandler<T> handler) {
    
    
        return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{
    
    clazz}, handler);
    }

    private static void generateProxyClass() {
    
    
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Hello.class.getInterfaces());
        String path = "/Users/nathan.yang/workspace/algorithm_Java/out/StuProxy.class";
        try (FileOutputStream fos = new FileOutputStream(path)) {
    
    
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类文件写入成功");
        } catch (Exception e) {
    
    
            System.out.println("写文件错误");
        }
    }
}

3. El principio de agencia dinámica

Inserte la descripción de la imagen aquí

  1. Proxy#newProxyInstance() El método es la entrada del proxy dinámico. Los pasos principales para generar objetos de proxy dinámico son los siguientes:

    1. getProxyClass0() Método para generar clase de proxy
    2. Después de obtener los InvocationHandlerobjetos de la clase de proxy en la referencia reflectante, llame al constructor para generar un objeto de proxy dinámico
    public static Object newProxyInstance(ClassLoader loader,
                                           Class<?>[] interfaces,
                                           InvocationHandler h)
         throws IllegalArgumentException
     {
          
          
         Objects.requireNonNull(h);
    
         final Class<?>[] intfs = interfaces.clone();
         final SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
          
          
             checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
         }
    
         /*
          * Look up or generate the designated proxy class.
          */
         Class<?> cl = getProxyClass0(loader, intfs);
    
         /*
          * Invoke its constructor with the designated invocation handler.
          */
         try {
          
          
             if (sm != null) {
          
          
                 checkNewProxyPermission(Reflection.getCallerClass(), cl);
             }
    
             final Constructor<?> cons = cl.getConstructor(constructorParams);
             final InvocationHandler ih = h;
             if (!Modifier.isPublic(cl.getModifiers())) {
          
          
                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
          
          
                     public Void run() {
          
          
                         cons.setAccessible(true);
                         return null;
                     }
                 });
             }
             return cons.newInstance(new Object[]{
          
          h});
         } catch (IllegalAccessException|InstantiationException e) {
          
          
             throw new InternalError(e.toString(), e);
         } catch (InvocationTargetException e) {
          
          
             Throwable t = e.getCause();
             if (t instanceof RuntimeException) {
          
          
                 throw (RuntimeException) t;
             } else {
          
          
                 throw new InternalError(t.toString(), t);
             }
         } catch (NoSuchMethodException e) {
          
          
             throw new InternalError(e.toString(), e);
         }
     }
    
  2. Proxy#getProxyClass0()De hecho, a partir de un método WeakCachepara obtener la clase de proxy que adquiere la lógica es que si no hay una clase de caché de proxy se llama ProxyClassFactory#apply()a ese tipo de cosas , para generar una clase de proxy instantáneo por la fábrica de clases de proxy, los siguientes pasos:

    1. Primero, verifique si el cargador de clases especificado puede cargar la interfaz de destino
    2. Determine el paquete donde se encuentra la clase de proxy y el nombre completo de la clase de proxy en función de condiciones como el paquete donde se encuentra la interfaz. El nombre de la clase de proxy es包名+$Proxy+id
    3. Por ProxyGenerator.generateProxyClass()la generación de una matriz de código de bytes para generar dinámicamente y cargar la clase de proxy
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
          
          
    
             Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
             for (Class<?> intf : interfaces) {
          
          
                 /*
                  * Verify that the class loader resolves the name of this
                  * interface to the same Class object.
                  */
                 Class<?> interfaceClass = null;
                 try {
          
          
                     interfaceClass = Class.forName(intf.getName(), false, loader);
                 } catch (ClassNotFoundException e) {
          
          
                 }
                 if (interfaceClass != intf) {
          
          
                     throw new IllegalArgumentException(
                         intf + " is not visible from class loader");
                 }
                 /*
                  * Verify that the Class object actually represents an
                  * interface.
                  */
                 if (!interfaceClass.isInterface()) {
          
          
                     throw new IllegalArgumentException(
                         interfaceClass.getName() + " is not an interface");
                 }
                 /*
                  * Verify that this interface is not a duplicate.
                  */
                 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
          
          
                     throw new IllegalArgumentException(
                         "repeated interface: " + interfaceClass.getName());
                 }
             }
    
             String proxyPkg = null;     // package to define proxy class in
             int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
             /*
              * Record the package of a non-public proxy interface so that the
              * proxy class will be defined in the same package.  Verify that
              * all non-public proxy interfaces are in the same package.
              */
             for (Class<?> intf : interfaces) {
          
          
                 int flags = intf.getModifiers();
                 if (!Modifier.isPublic(flags)) {
          
          
                     accessFlags = Modifier.FINAL;
                     String name = intf.getName();
                     int n = name.lastIndexOf('.');
                     String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                     if (proxyPkg == null) {
          
          
                         proxyPkg = pkg;
                     } else if (!pkg.equals(proxyPkg)) {
          
          
                         throw new IllegalArgumentException(
                             "non-public interfaces from different packages");
                     }
                 }
             }
    
             if (proxyPkg == null) {
          
          
                 // if no non-public proxy interfaces, use com.sun.proxy package
                 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
             }
    
             /*
              * Choose a name for the proxy class to generate.
              */
             long num = nextUniqueNumber.getAndIncrement();
             String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
             /*
              * Generate the specified proxy class.
              */
             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                 proxyName, interfaces, accessFlags);
             try {
          
          
                 return defineClass0(loader, proxyName,
                                     proxyClassFile, 0, proxyClassFile.length);
             } catch (ClassFormatError e) {
          
          
                 /*
                  * A ClassFormatError here means that (barring bugs in the
                  * proxy class generation code) there was some other
                  * invalid aspect of the arguments supplied to the proxy
                  * class creation (such as virtual machine limitations
                  * exceeded).
                  */
                 throw new IllegalArgumentException(e.toString());
             }
         }
    
  3. Reflexión parámetro adquirido InvocationHandler.classconstructor de clase de proxy , de hecho, es Proxyel constructor parametrizado, llame al constructor cons.newInstance(new Object[]{h})para generar un objeto proxy

    protected Proxy(InvocationHandler h) {
          
          
         Objects.requireNonNull(h);
         this.h = h;
     }
    
  4. El siguiente código puede generar la clase de proxy cargada en la JVM en un archivo de clase, y luego puede usar la herramienta de descompilación para ver el código fuente de la clase de proxy

    private static void generateProxyClass() {
          
          
         byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Hello.class.getInterfaces());
         String path = "/Users/nathan/workspace/algorithm_Java/out/StuProxy.class";
         try (FileOutputStream fos = new FileOutputStream(path)) {
          
          
             fos.write(classFile);
             fos.flush();
             System.out.println("代理类文件写入成功");
         } catch (Exception e) {
          
          
             System.out.println("写文件错误");
         }
     }
    
  5. El código fuente de la clase de proxy generado es el siguiente, es obvio que puede ver el principio de implementación de proxy dinámico de esta clase:

    1. Por staticbloque de códigoClase de proxyCada paquete en el método del Methodobjeto, el método de generación de tablas
    2. Objeto proxyEjecuta el método de clase de proxy con el mismo nombre, clase principal Proxyapuntando a retener InvocationHandleruna referencia de objeto para llamar a InvocationHandler#invoke()un método, proxies dinámicos completos
    public final class $Proxy0 extends Proxy implements IHello {
          
          
     private static Method m1;
     private static Method m3;
     private static Method m2;
     private static Method m0;
    
     public $Proxy0(InvocationHandler var1) throws  {
          
          
         super(var1);
     }
     
     public final String sayHi() throws  {
          
          
         try {
          
          
           // 父类 Proxy 保留的指向 InvocationHandler 对象的引用调用 invoke() 方法
             return (String)super.h.invoke(this, m3, (Object[])null);
         } catch (RuntimeException | Error var2) {
          
          
             throw var2;
         } catch (Throwable var3) {
          
          
             throw new UndeclaredThrowableException(var3);
         }
     }
    
     public final String toString() throws  {
          
          
         try {
          
          
             return (String)super.h.invoke(this, m2, (Object[])null);
         } catch (RuntimeException | Error var2) {
          
          
             throw var2;
         } catch (Throwable var3) {
          
          
             throw new UndeclaredThrowableException(var3);
         }
     }
    
    ......
    
    // 方法表
     static {
          
          
         try {
          
          
             m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
             m3 = Class.forName("ServiceProxy$IHello").getMethod("sayHi");
             m2 = Class.forName("java.lang.Object").getMethod("toString");
             m0 = Class.forName("java.lang.Object").getMethod("hashCode");
         } catch (NoSuchMethodException var2) {
          
          
             throw new NoSuchMethodError(var2.getMessage());
         } catch (ClassNotFoundException var3) {
          
          
             throw new NoClassDefFoundError(var3.getMessage());
         }
     }
    }
    

Supongo que te gusta

Origin blog.csdn.net/weixin_45505313/article/details/106399906
Recomendado
Clasificación