JDK Proxy dynamic proxy resolution

Opening

  • When reading the open source code framework, I often go to the application scenario of Proxy dynamic Proxy and take the time to study it.
  • Analyze the calling process with the help of Proxy demo and decompile dynamic Proxy code.
  • With the help of the source code of Proxy dynamic Proxy class, the generation process of dynamic class is analyzed.

Proxy application scenario

package com.sunboy;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;

public class ViewProxyClass {

    public static void main(String[] args) throws Exception {

        Game gamePlayer = new GamePalyer("Zhang San");

        ClassLoader classLoader = gamePlayer.getClass().getClassLoader();
        Class<?>[] interfaces = gamePlayer.getClass().getInterfaces();
        // Basic operations of generating dynamic proxy
        Game proxy = (Game) Proxy.newProxyInstance(classLoader, interfaces, new GameInvocation(gamePlayer));
        proxy.paly();

        // Debugging method for generating source code of dynamic agent
        byte[] bts = ProxyGenerator.generateProxyClass("$GameProxy", interfaces);
        FileOutputStream fos = new FileOutputStream(new File("D:/tmp/$GameProxy.class"));
        fos.write(bts);
        fos.flush();
        fos.close();
    }

    // Definition of interface
    interface Game {
        public void paly();
    }

    // Implementation of interface
    static class GamePalyer implements Game {

        private String name;

        public GamePalyer(String name) {
            this.name = name;
        }

        @Override
        public void paly() {
            System.out.println(this.name + " playing ...");
        }
    }

    // Encapsulation object of dynamic proxy
    static class GameInvocation implements InvocationHandler {

        private Game target;

        public GameInvocation(Game target) {
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(target + "Represented");
            method.invoke(this.target, args);
            return proxy;
        }
    }
}
  • The Proxy dynamic Proxy class is based on the Interface dimension, and the Proxy class needs to implement the Interface. For example, the gamepayer class mentioned in the case implements the Game Interface.
  • The dynamic Proxy encapsulation class of Proxy needs to implement the InvocationHandler interface, such as the GameInvocation object in the case.
  • The dynamic Proxy class is generated through Proxy Newproxyinstance. The code of dynamic Proxy class is generated by the dynamic definition of Proxy interface, which can be generated through proxygenerator Generateproxyclass ("$gameproxy", interfaces) generates a Proxy class and saves it to the class file for decompilation and observation.
  • The first parameter Object of InvocationHandler refers to the dynamically generated proxy class, the second parameter is the proxy method, and the third-party parameter is the dynamic input parameter.

Proxy dynamic proxy class

import com.sunboy.ViewProxyClass.Game;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $GameProxy extends Proxy implements Game {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $GameProxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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);
        }
    }

    public final void paly() 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  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    // Load dynamic proxy class
    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.sunboy.ViewProxyClass$Game").getMethod("paly");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}


public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;
    protected InvocationHandler h;

     protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
}
  • The dynamic Proxy class GameProxy inherits the Proxy and implement s the Proxy interface (such as Game in the case).
  • The parameter of the constructor $GameProxy(InvocationHandler var1) of the dynamic Proxy class GameProxy is the InvocationHandler object and passed to the parent class Proxy.
  • Dynamic proxy class GameProxy through class Forname to load the proxy interface, and get the proxy Method through getMethod.
  • The dynamic proxy class overrides the Method to be proxied and passes super h. Invoke() calls the invoke Method of InvocationHandler.
  • The core of dynamic proxy class is to overload the proxy method and call the invoke method of proxy object Invocation inside the overloaded method. The implementation code of the agent is in the Invocation

Dynamic agent factory

public class Proxy implements java.io.Serializable {

    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        private static final String proxyClassNamePrefix = "$Proxy";

        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            // Check the legitimacy of the proxy Interface
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                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");
                }

                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }

                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            // Check the validity of the package
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            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) {
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            // 1. Class to generate dynamic proxy
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);

            try {
                // 2. Load class of dynamic proxy
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {

                throw new IllegalArgumentException(e.toString());
            }
        }
    }
}
  • ProxyGenerator.generateProxyClass() is responsible for generating dynamic proxy classes.
  • defineClass0 is responsible for loading the newly generated dynamic proxy class and returning it.
  • ProxyClassFactory is responsible for dynamically generating the factory of proxy classes.

Added by lpxxfaintxx on Sat, 26 Feb 2022 05:39:52 +0200