Dubbo進階(十)- Dubbo動態對象生成過程詳細分析

上一篇文章仔細的分析了 ,由 refer 方法 獲取的 代理對象的過程,以及最後獲取到的代理對象的結構,但是結尾比較草,本節仔細分析 字節碼生成的代理對象。

結構

上一篇文章結尾草草的給出了 代理對象的結構圖,但是並沒有對(T) PROXY_FACTORY.getProxy(invoker)進行 整體上的分析:
在這裏插入圖片描述
首先在 Dubbo 的 ProxyFactory中,有兩個字節碼工具類,一個是JdkProxyFatory,另一個是 JavassistProxyFactory
具體SPI 文件如下:

stub=org.apache.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
jdk=org.apache.dubbo.rpc.proxy.jdk.JdkProxyFactory
javassist=org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory

並且默認使用的是 JavassistProxyFactory 作爲字節碼工具。
StubProxyFactoryWrapper 則主要是要執行優先於 ProxyFactory 的一些邏輯,這裏主要是在 getProxy 對 配置了 stublocal 屬性 進行一些額外處理。

在 JavassistProxyFactory中的構造方法如下:

    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

上面構造方法主要是使用字節碼工具,將多個 interfaces對象(包括了 EchoService)生成一個代理對象,再使用 InvokerInvocationHandler 包裝起來。

ProxyFactory$Adaptive 源碼

通過一定方式,獲取到了 ProxyFactory$Adaptive 源碼,便於我們後面分析:

package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
    public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
        if (arg2 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg2;
        String extName = url.getParameter("proxy", "javassist");
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getInvoker(arg0, arg1, arg2);
}
    public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("proxy", "javassist");
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getProxy(arg0, arg1);
}
    public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("proxy", "javassist");
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getProxy(arg0);
}
}

PROXY_FACTORY.getProxy(invoker) 詳解

AbstractProxyFactory 代碼示例:


    @Override
    public <T> T getProxy(Invoker<T> invoker) throws RpcException {
    // 不是泛化調用
        return getProxy(invoker, false);
    }

    @Override
    public <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException {
        Class<?>[] interfaces = null;
        String config = invoker.getUrl().getParameter(INTERFACES);
        if (config != null && config.length() > 0) {
            String[] types = COMMA_SPLIT_PATTERN.split(config);
            if (types != null && types.length > 0) {
                interfaces = new Class<?>[types.length + 2];
                interfaces[0] = invoker.getInterface();
                interfaces[1] = EchoService.class;
                for (int i = 0; i < types.length; i++) {
                    // TODO can we load successfully for a different classloader?.
                    interfaces[i + 2] = ReflectUtils.forName(types[i]);
                }
            }
        }
        if (interfaces == null) {
        // 加入EchoService.class
            interfaces = new Class<?>[]{invoker.getInterface(), EchoService.class};
        }

        if (!GenericService.class.isAssignableFrom(invoker.getInterface()) && generic) {
        // 加入GenericService.class
            int len = interfaces.length;
            Class<?>[] temp = interfaces;
            interfaces = new Class<?>[len + 1];
            System.arraycopy(temp, 0, interfaces, 0, len);
            interfaces[len] = com.alibaba.dubbo.rpc.service.GenericService.class;
        }

        return getProxy(invoker, interfaces);
    }

以下將一步一步,鑽進getProxy 看其具體實現:

  1. ProxyFactory$Adaptive,會先檢查參數。
  2. 通過proxy 參數獲取使用的代理工廠,沒有則使用默認的 javassist,使用 ExtensionLoader 的 SPI 方式獲取。
  3. Adaptive 類中,獲取的 extension 爲 使用 StubProxyFactoryWrapper 包裝後的 ProxyFactory,所以會先執行StubProxyFactoryWrappergetProxy 方法,而是否會執行 具體 ProxyFactorygetProxy 方法,則需要看 Wrapper 類的邏輯。
    在這裏插入圖片描述
  4. 進入 StubProxyFactoryWrappergetProxy,最終會先調用 AbstractProxyFactory,這裏面的getProxy 方法主要是判斷是否需要往代理生成類中再加入 GenericService 用於泛化調用。
  5. 而後調用子類的getProxy,這裏使用 JavassistProxyFactory 爲例:
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }
  1. 使用字節碼工具,將傳入的interfaces類,以及 參數 InvokerInvocationHandler,動態生成一個新的代理類並返回。
    這裏爲什麼是多個呢?在使用Dubbo 的EchoService 及 泛化調用時,都是使用強轉的形式,所以這裏就傳入多個interfaces,才保證了強轉不會出錯。
  2. 在使用 javassist 構造 Proxy.getProxy 時候,截取了字節碼class 反編譯後的 java 代碼,可以便於理解
    在具體的 JavassistProxyFactorypublic <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) 依次會生成兩個類:一個是Proxy0的包裝類,另一個是 proxy0,即具體功能存放類。

proxy0 代理類

public class proxy0 implements DC, EchoService, HelloService {
    public static Method[] methods;
    private InvocationHandler handler;

    public String hello(String var1) {
        Object[] var2 = new Object[]{var1};
        Object var3 = this.handler.invoke(this, methods[0], var2);
        return (String)var3;
    }

    public Object $echo(Object var1) {
        Object[] var2 = new Object[]{var1};
        Object var3 = this.handler.invoke(this, methods[1], var2);
        return (Object)var3;
    }

    public proxy0() {
    }

    public proxy0(InvocationHandler var1) {
        this.handler = var1;
    }
}

Proxy0 代理類

public class Proxy0 extends Proxy implements DC {
    public Object newInstance(InvocationHandler var1) {
        return new proxy0(var1);
    }

    public Proxy0() {
    }
}

分析在 JavassistProxyFactory 中的 以下方法:

    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

從上面代碼中可以有以下收穫:

  1. Proxy.getProxy(interfaces) 返回的是一個 Proxy0 對象,注意是大寫的P
  2. 最後調用 Proxy0newInstance 方法,實例化一個包裝類型的 proxy0,注意是小寫的
  3. dubbo 裏面的 EchoService 或者 泛化調用,都是通過javassist 字節碼工具,以實現方法形式實現,所以纔可以在業務層面強轉成功。
  4. 對於返回的代理類,也實現對應 refer 的接口,例如上述實現的 HelloService
  5. DC 是dynamic code 縮寫,只是一個標識,說明是動態生成的類。
  6. 對於手動執行方法,都是調用 InvokerInvocationHandlerinvoke 方法

而對於 InvokerInvocationHandler 中invoker 及後面調用邏輯,且看下一篇文章分析。

覺得博主寫的有用,不妨關注博主公衆號: 六點A君。
哈哈哈,Dubbo小吃街不迷路:
在這裏插入圖片描述

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