为了弄懂Retrofit源码系列文章——Java动态代理到底是怎么回事

Hello,我是来自58同城的一名Android开发工程师,在58集团从事SDK的开发工作。

欢迎关注我的公众号,在这里可以随时找到我,这里会不定期推送一些时下最热门的技术文章和互联网行业工作心路历程。


自己的接口怎么Duang~的一下就可以跑起来了???

上一次我们已经分析了Java的ClassLoader,相信大家已经对类的加载机制有了一定的了解,一些比较重要的概念包括:类加载器的工作流程,如何控制类的加载过程(Android热修复原理),以及类加载过程中的双亲委派是怎么回事。 

其实说到底,Java的动态代理类还是在运行时帮我们生成了一个实现了接口的类,这个类就是代理类,然后通过这个代理类去做一些invoke中指定的事,相当于编写代理类的这个过程由Java虚拟机来帮你做了,可以让你把精力都集中于业务的实现,及大的解放了生产力。

现在,暂时让我们先抛开Retrofit中的动态代理,自己撸一个动态代理的小demo来做个测试。

代码我已经上传到Github上了,大家可以clone下来体验一下这个生成的过程,对理解Java动态代理非常有用。

GitHubDemo

在这里贴一下代码:

 

public interface human {
    void eat();
    void run();
}

 

public class Tom implements human{
    public Tom(){}
    @Override
    public void eat() {
        System.out.println("humanImpl eating");
    }
    @Override
    public void run() {
        System.out.println("humanImpl running");
    }
}

 

public class CSHTest {
    public static void main(String[] args) {
        //能不能在本地看见$Proxy.class主要就是这句,可以在自己的项目中加上这句话   System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        human human = new Tom();
        human human1 = (human) Proxy.newProxyInstance(human.getClass().getClassLoader(), human.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    return method.invoke(human, null);
            }
        });
        human1.run();
        System.out.println("$Proxy0.class全名: "+Proxy.getProxyClass(human.class.getClassLoader(), human.class));
    }
}

通过代码大家可以看到,Tom类实现了human接口中的方法,并且通过将human接口传入Proxy.newProxyInstance来实现了动态代理类的生成,最后通过调用动态代理对象方法,实现了动态代理的效果。

相信大家也看到了,在main函数的第一行,强制让生成的代理类进行展示,那么这个代理类在哪呢?

大家可以在自己的电脑上试一下,就像我代码里写的那样,强制让代理类进行显示,然后在电脑上搜索一下:$Proxy.class这个文件。

如果已经能够在你的硬盘上看到这个文件,可以把它删掉,再跑一下程序试试。

我的是在这里:

 

这个就是由虚拟机在运行时帮我们生成的代理类。

那这个类里面长什么样呢?

public final class $Proxy0 extends Proxy implements human {
    private static Method m1;
    private static Method m3;
    private static Method m4;
    private static Method m2;
    private static Method m0;
    public $Proxy0(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 void run() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final void eat() throws  {
        try {
            super.h.invoke(this, m4, (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);
        }
    }
    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);
        }
    }
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("human").getMethod("run");
            m4 = Class.forName("human").getMethod("eat");
            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());
        }
    }
}

可以看到,在$Proxy0的构造函数中传入了InvokeHandler,没错,就是我们在Proxy.newProxyInstance中创建的new InvokeHandler()。然后在他的静态方法区里面,通过反射的方式,得到了我们在接口中定义的方法,并作为参数进行了向下传递。

最后我们还能看到,这个类继承了我们写的接口,human,并且实现了里面的方法,在方法实现里

super.h.invoke(this, m3, (Object[])null);

能够看到,它通过父类Proxy来invoke了InvokeHandler中的invoke方法,也就相当于帮我们做了一次传统意义上的静态代理.


怎么样,是不是感觉很神奇?

好了,这就是一个简单的动态代理测试,接下来,让我们深入源码,来看看这个动态代理到底是如何工作的,以及动态代理类是如何生成的。

以下代码基于原生Java 1.8展示

让我们通过newProxyInstance来开启源码之路:


public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
                                      InvocationHandler h)
throws IllegalArgumentException
{
    Objects.requireNonNull(h);
    final Class<?>[] intfs = interfaces.clone();//1
    。。。

    /*
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass0(loader, intfs);//2
    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        。。。
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
       。。。
        return cons.newInstance(new Object[]{h});
    }

。。。}

通过源码可以看到,在newProxyInstance里面先是做了一把判空校验,然后通过clone方法拿到了接口的深度拷贝。并将它传到了getProxyClass0的方法里。

在getProxyClass0方法里又调用了proxyClassCache.get(loader, interfaces)方法。

//WeakCache.java

public V get(K key, P parameter) {
    Objects.requireNonNull(parameter);
    。。。
// create subKey and retrieve the possible Supplier<V> stored by that
    // subKey from valuesMap
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));//1
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;
    while (true) {
        if (supplier != null) {
            // supplier might be a Factory or a CacheValue<V> instance
            V value = supplier.get();
            if (value != null) {
                return value;//2
            }
        }
        // else no supplier in cache
        // or a supplier that returned null (could be a cleared CacheValue
        // or a Factory that wasn't successful in installing the CacheValue)
        // lazily construct a Factory
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }

//3
        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                // successfully installed Factory
                supplier = factory;
            }
            // else retry with winning supplier
        } else {
            if (valuesMap.replace(subKey, supplier, factory)) {
                // successfully replaced
                // cleared CacheEntry / unsuccessful Factory
                // with our Factory
                supplier = factory;
            } else {
                // retry with current supplier
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

在代码中注释为1的位置我们可以看到,通过subKeyFactory.apply方法拿到了一个subkey对象,并最终通过while循环的supplier.get方法得到了代理类对象,进行了返回。

到这里,核心的问题也就变成了subKeyFactory.apply(key, parameter)到底做了些什么,让我们进入apply方法来一探究竟:

subKeyFactory本身是一个BiFunction接口,通过查看其实现类,我们能够看到如下的几个实现类:

 

在这里,我们就进入运行时的实现类:ProxyClassFactory来看看。

@Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
        。。。

        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) {
            }
            。。。

String proxyName = proxyPkg + proxyClassNamePrefix + num;

try {
     return defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
}

        /*
         * Generate the specified proxy class.
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        。。。

    }
}

这里我帮大家隐去了一些非核心代码,我们来重点关注两个地方:

  1. proxyName:看着它的定义方式,是不是有几分似曾相识的感觉?没错,我们的$Proxy0.java文件名就是从这里生成的,现在已经非常接近真相了。
  2. defineClass0:这里就是生成我们$Proxy0.java的地方,它是一个Native方法,也就是说后续的生成文件操作就是由C++部分来完成的。当然,这都不重要,重要的是我们现在已经知道$Proxy0.java文件到底是怎么得来的,这就足够了。

经过了上面一系列逻辑的流转调用后,从newProxyInstance里面我们就得到java为我们生成的代理类了。

现在,让我们回过头来看看,invoke方法到底是怎么被调用的,这里我截取了一个方法来举例,实现的道理都是想通的。

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

在这里我们能够看到,当我们从外部调用Java为我们生成的代理类方法时,他的内部其实是调用了我们事先在InvokeHandler里面定义好了的invoke方法。可以看到,从invoke方法这边传过去的参数不论是类型和数量都是和InvokeHandler一致的。

那么问题来了,这几个参数代表的意义是什么

new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(human, null);
    }
}

从定义处我们能够看到,传进来的值分别是,当前的代理类,当前的代理方法和代理方法所需的参数。然后当我们执行invoke方法的时候,就可以通过调用method的invoke方法,拿到代理方法的返回值了。

通过以上的分析我们可以看到,其实Java的动态代理与我们传统方式的静态代理,区别就在于“手动”和“自动”的区别,有了Proxy.newProxyInstance的加持,可以让我们舍弃传统方式大量的代理类编写,专注于业务与功能的实现,提升了开发效率。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章