爲了弄懂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的加持,可以讓我們捨棄傳統方式大量的代理類編寫,專注於業務與功能的實現,提升了開發效率。

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