The underlying principle of Java dynamic proxy

1. Agency Model

代理模式Is one of the commonly used design patterns, its characteristic isThe proxy class has the same interface as the proxy class. The proxy class can perform pre- and post-processing for the execution of the proxy class method to enhance the proxy class method

Insert picture description here

The class structure of the proxy model is usually as shown in the figure above. There will be an association relationship between the proxy class and the proxy class. An object of the proxy class holds an object of the proxy class. The object of the proxy class itself does not really implement the service, but provides specific services by calling the relevant methods of the proxy class object

2. Dynamic proxy usage

The proxy class is not defined in the Java code, but is dynamically generated at runtime according to the "instructions" in the Java code ( 字节码由JVM在运行时动态生成而非预存在任何一个 .class 文件中),This proxy method of creating proxy classes while the program is running is called动态代理, Its advantage is that it can conveniently process the functions of the proxy class. This is because all the methods executed by the proxy are InvocationHandler#invoke()called through methods, which is equivalent to putting a shell on all methods of the proxy class, so as long as this method is processed uniformly, all the proxy methods can be the same Operated

The following code shows the simple use of dynamic proxy, the basic steps are as follows:

  1. Define a public interface, in this case IHello, there is an abstract method in the interface
  2. Entity class defines implements a common interface as the proxy class, the present embodiment is the proxy class Helloimplements IHellothe interface, the abstract method override interface
  3. Defines implements a InvocationHandlermethod interception class interface, rewriting invoke()method to implement the processing logic interception proxy class is executed when the method of
  4. By Proxy.newProxyInstance()generating a proxy object methods, the implementation of an interface method after the proxy object can hold
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. The principle of dynamic agency

Insert picture description here

  1. Proxy#newProxyInstance() The method is the entrance of the dynamic proxy. The main steps to generate dynamic proxy objects are as follows:

    1. getProxyClass0() Method to generate proxy class
    2. After obtaining the proxy class InvocationHandlerobjects into the reference reflective call the constructor to generate dynamic proxy object
    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()In fact, from a method WeakCacheto obtain the proxy class that acquires the logic is that if there is no proxy cache class that kind of thing is called ProxyClassFactory#apply(), to generate a proxy class instant by proxy class factory, the following steps:

    1. First verify whether the target interface can be loaded by the specified class loader
    2. Determine the package where the proxy class is located and the fully qualified name of the proxy class based on conditions such as the package where the interface is located. The proxy class name is包名+$Proxy+id
    3. By ProxyGenerator.generateProxyClass()generating a bytecode array to dynamically generate and load the proxy class
    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. Reflection parameter acquired proxy class InvocationHandler.classconstructor, in fact, is Proxythe parameterized constructor, call the constructor cons.newInstance(new Object[]{h})to generate a proxy object

    protected Proxy(InvocationHandler h) {
          
          
         Objects.requireNonNull(h);
         this.h = h;
     }
    
  4. The following code can output the proxy class loaded in the JVM into a class file, and then you can use the decompiler tool to view the source code of the proxy class

    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. The generated proxy class source code is as follows, it is obvious that you can see the principle of dynamic proxy implementation of this class:

    1. By staticcode blockProxy classEach package in the method of Methodthe object, the table generation method
    2. Proxy objectExecutes proxy class method with the same name, parent class by Proxypointing to retain InvocationHandleran object reference to call InvocationHandler#invoke()a method, complete dynamic proxies
    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());
         }
     }
    }
    

Guess you like

Origin blog.csdn.net/weixin_45505313/article/details/106399906