JDK 的动态代理相关分析

最近做个动态代理测试,鬼畜地写了如下代码:

public interface SayHello {  
    String say(String message);
}
public class SayHelloInvocationHandler implements InvocationHandler {

    private SayHello target;

    public SayHelloInvocationHandler(SayHello target) {
        this.target = target;
    }

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

        if(StringUtils.equals(method.getName(), "say")) {

                System.out.println(proxy.getClass());     //打印第一行
                System.out.println(proxy.toString());     //打印第二行

                return method.invoke(target, args);
        } else {

            return method.invoke(proxy, args);    //鬼畜行
        }
    }

都知道了,对,没错,控制台输出打印第一行的执行结果后就在鬼畜行的进行死循环了!

class com.sun.proxy.$Proxy21  

经过查找原因,死循环触发行是打印第二行执行,之后进入鬼畜行

于是就是想看看这个class com.sun.proxy.$Proxy21的内容到底是个什么!

经搜索,原来 JDK 生成的动态代理类也是可以写入磁盘的,需要修改系统属性sun.misc.ProxyGenerator.saveGeneratedFilestrue

JDK 动态代理类生成是由sun.misc.ProxyGenerator类来完成;查看内此类中的关键两行,就知道了:

……
private static final boolean saveGeneratedFiles = ((Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();  
……
/** 
 * Generate a proxy class given a name and a list of proxy interfaces.
 */
public static byte[] generateProxyClass(final String name,  
                                        Class[] interfaces)
{
    ProxyGenerator gen = new ProxyGenerator(name, interfaces);
    final byte[] classFile = gen.generateClassFile();
    if (saveGeneratedFiles) {
……

了解这些,我将$Proxy21.class内容拿到并进行反编译,得到如下结果:

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

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

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

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String say(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, 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);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m3 = Class.forName("manbu.demo.proxy.SayHello").getMethod("say", new Class[]{Class.forName("java.lang.String")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

原来生成的动态中除了对接口中定义的方法进行代理处理外,还对equalshashCodetoString方法进行也都进行了代理处理;这就解释了鬼畜行的问题了;