Dubbo源碼分析——Dubbo SPI

Dubbo採用微內核+插件模式的設計原則,微內核負責組裝插件,也就是Dubbo的全部功能點均可被用戶自定義擴展所替換。微內核是由Dubbo SPI機制實現的,所以瞭解Dubbo SPI是很是重要的。java

Dubbo SPI簡介

Dubbo SPI從JDK標準的SPI (Service Provider Interface) 擴展點發現機制增強而來,改進了JDK標準的 SPI 的如下問題:ruby

  • 沒法獲取指定的擴展實現 。JDK 標準的 SPI 會一次性實例化擴展點全部實現,若是有擴展實現初始化很耗時,但若是沒用上也加載,會很浪費資源。app

  • 若是擴展點加載失敗,連擴展點的名稱都拿不到了。好比:JDK標準的ScriptEngine,經過getName() 獲取腳本類型的名稱,但若是RubyScriptEngine由於所依賴的 jruby.jar不存在致使 RubyScriptEngine 類加載失敗,這個失敗緣由被吃掉了,和ruby對應不起來,當用戶執行ruby腳本時,會報不支持ruby,而不是真正失敗的緣由。ide

  • 增長了對擴展點IoC和AOP的支持,一個擴展點能夠直接setter注入其它擴展點。工具

若是不瞭解JDK SPI機制,能夠看我寫的JDK SPI源碼詳解源碼分析

Dubbo SPI機制源碼分析

JDK SPI機制是由java.util.ServiceLoader這個工具類實現的,一樣Dubbo也提供了相似的類(com.alibaba.dubbo.common.extension.ExtensionLoader)來實現的Dubbo SPI機制。post

下面的內容主要講解ExtensionLoader如何獲取指定的擴展點和自適應擴展點url

獲取指定的擴展點

經過獲取DubboProtocol爲例,其代碼以下:spa

Protocol protocol  = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
複製代碼

經過具體的源碼分析,其運行過程以下:插件

一、建立ExtensionLoader實例

// Dubbo SPI只支持接口而且被@SPI修飾
ExtensionLoader loader = new ExtensionLoader(
    Protocol.class,
    ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
)
複製代碼

二、獲取擴展點全部實現類的Class對象

/** * 加載META-INF/services/、META-INF/dubbo/internal/、META-INF/dubbo/目錄下type.getName文件 * 並解析內容,而後將type基本實現類(不包括包裝類,沒有Adaptive註解)存儲在extensionClasse中。 */
private Map<String, Class<?>> loadExtensionClasses() {
    Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    loadFile(extensionClasses, DUBBO_DIRECTORY);
    loadFile(extensionClasses, SERVICES_DIRECTORY);
    return extensionClasses;
}
複製代碼

三、建立DubboProtocol對象並裝配其依賴的對象

// 獲取名爲dubbo對應的擴展點實現類
Class<?> clazz = getExtensionClasses().get(name);
// 建立對象
T instance = clazz.newInstance();
// 自動裝配
injectExtension(instance);
複製代碼

四、返回ProtocolListenerWrapper對象

Set<Class<?>> wrapperClasses = cachedWrapperClasses;
// 循環遍歷初始化包裝類
if (wrapperClasses != null && wrapperClasses.size() > 0) {
    for (Class<?> wrapperClass : wrapperClasses) {
        instance = injectExtension((T) wrapperClass.getConstructor(type)
            .newInstance(instance));
    }
}
return instance;
複製代碼

自動注入過程獲取須要依賴的對象,是經過ExtensionLoader.objectFactory對象獲取的。

獲取自適應擴展點

經過獲取Procotol自適應擴展點爲例,其代碼以下:

Protocol protocol  = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
複製代碼

其過程以下:

// 建立Protocol自適應擴展點實例
private T createAdaptiveExtension() {
    return injectExtension((T) getAdaptiveExtensionClass().newInstance());
}

/** * 之因此貼出這段代碼,由於獲取自適應擴展點會觸發獲取擴展點全部實現類的Class對象。目前 * dubbo只有被@Adaptive註解的類僅有AdaptiveCompiler和AdaptiveExtensionFactory,所以除了 * Complie和ExtensionFactory外都須要動態建立其自適應擴展點的Class */
private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
    
// 建立自適應擴展點類Class對象
private Class<?> createAdaptiveExtensionClass() {
    String code = createAdaptiveExtensionClassCode();
    ClassLoader classLoader = findClassLoader();
    com.alibaba.dubbo.common.compiler.Compiler compiler = 
    ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class)
    .getAdaptiveExtension();
    return compiler.compile(code, classLoader);
}
複製代碼

動態生成的自適應擴展點Protocol$Adpative類源碼以下:

public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {

    public void destroy() {
        throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
        if (arg1 == null) 
            throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
       
        com.alibaba.dubbo.rpc.Protocol extension = 
        (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader
            .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
            .getExtension(extName);
        return extension.refer(arg0, arg1);
    }

    public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
        if (arg0 == null)
         throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        
        com.alibaba.dubbo.rpc.Protocol extension = 
        (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader
            .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
            .getExtension(extName);
        return extension.export(arg0);
    }
}
複製代碼

能夠發現自適應擴展點的做用:經過URL的參數信息獲取對應擴展點,從而實現方法動態調用。對應Dubbo基本設計原二:

採用 URL 做爲配置信息的統一格式,全部擴展點都經過傳遞 URL 攜帶配置信息。

相關文章
相關標籤/搜索