Dubbo ExtensionLoader源碼解讀

ExtensionLoader提供了靜態工廠方法public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type)來獲取ExtensionLoader的實例。
理解ExtensionLoader要注意區分ExtensionLoader與Extension的概念。從名字上也可以看出ExtensionLoader是Extension的加載器,可以看成是工廠類。在調用getActivateExtension、getLoadedExtension、getExtension、getDefaultExtension、getAdaptiveExtension等方法的時候,得到的是相應的extension的對象實例。

在創建ExtensionLoader實例的時候,會創建該ExtensionLoader對應Extension的工廠類ExtensionFactory。

private ExtensionLoader(Class<?> type) {
    this.type = type;
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

其中ExtensionFactory也使用了ExtensionLoader的靜態工廠方法來獲取。獲取ExtensionFactory的ExtensionLoader後會調用ExtensionLoader.getAdaptiveExtension方法來獲取ExtensionFactory對象。

對於getAdaptiveExtension方法,首先從cachedAdaptiveInstance獲取緩存的adaptive的類型爲type的實例。如果不存在,會調用createAdaptiveExtension方法來創建adaptive的type類實例。

createAdaptiveExtension函數調用了getAdaptiveExtensionClass方法來獲取Adaptive的Extension類。至於什麼是Adaptive的Extension,稍後解析getAdaptiveExtensionClass方法的時候便可以清楚了。在獲取了Extension類後,就會調用injectExtension方法來反射注入Extension實例對象的屬性值。對於Extension實例中各屬性值的獲取,是使用objectFactory對象的工廠方法<T> T getExtension(Class<T> type, String name);來獲取。
Dubbo中實現的ExtensionFactory主要有三個:

  • com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
  • com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
  • com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory

下面說一下getAdaptiveExtensionClass方法的作用。

private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

可以看出首先調用getExtensionClasses方法,getExtensionClasses方法的作用主要是去獲取所有配置的type類型的實現類。如果cachedClasses不爲空,那麼直接返回cachedClasses。如果cachedClasses爲空,則會調用loadExtensionClasses來加載extension類。首先會獲取type上的@SPI註解。如果註解不爲空,那麼獲取註解的value值賦值給cachedDefaultName。然後依次掃描classpath以及所依賴jar包中”META-INF/dubbo/internal/”、”META-INF/dubbo/”、”META-INF/services/”路徑下配置的文件進行掃描,掃描完成後將結果賦值給cachedClasses。這個掃描是通過loadFile函數來實現的。

通過查看loadFile方法代碼就可以清楚dubbo擴展配置的方法。配置文件的名稱是type的全限定名。裏面的內容是name=value的形式。name是名稱,value是type的實現類。例如對於type爲ExtensionFactory.class的配置,在dubbo的jar包中”/META-INF/dubbo/internal”下有一個名字爲”com.alibaba.dubbo.common.extension.ExtensionFactory”的文件,其中內容如下:

spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactor

這三個就是對應的ExtensionFactory的實現類。

同時loadFile在掃描的過程中,會看對應的類上是否標註了@Adaptive註解。如果標註了,那麼就將cachedAdaptiveClass賦值爲該類。並且cachedAdaptiveClass是唯一的,不能多於一個。

對於沒有標註@Adaptive註解的類,首先嚐試查找類是否有以type爲參數的構造函數,如果有,說明這個類是type類型的wrapper類,將該類存入cachedWrapperClasses中。如果沒有該構造函數,就獲取默認構造函數,從這裏可以看出key可以省略。name可以從註解@Extension中獲取,或者實現類的simpleName中獲取,如果該類的simpleName以type的simpleName結尾,那麼就會去除來得到name。然後會根據name來將該類與name的對應關係存入cachedNames中,並將類存入extensionClasses中。另外,如果類標註了@Activate註解,還會將該類存入cachedActivates中。

講完了getExtensionClasses方法的作用,回來繼續看getAdaptiveExtensionClass方法,調用完getExtensionClasses方法後,首先判斷cachedAdaptiveClass是否爲空,之前在getExtensionClasses方法中可以看到,如果相應的類標註了@Adaptive註解,那麼cachedAdaptiveClass就不爲空,這時候getAdaptiveExtensionClass直接就將cachedAdaptiveClass返回。如果cachedAdaptiveClass爲空,那麼會繼續調用createAdaptiveExtensionClass來動態編譯生成cachedAdaptiveClass。

這裏具體來解析下createAdaptiveExtensionClass的作用。

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);
}

可以看出首先調用createAdaptiveExtensionClassCode來生成代碼字符串,然後利用ExtensionLoader獲取Adaptive的Compiler實例,Compiler的Apative類對應的是com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler。然後動態編譯獲取相應的類。

對於createAdaptiveExtensionClassCode方法的機制是這樣的:

獲取type類型接口上定義的method,然後找出這些method中有@Adaptive註解的method,然後根據該method的定義來動態生成實現方法。看代碼可以看出動態創建Adaptive的類有一定的限制條件。即參數中必須要有URL類型的參數,或者某個參數的get方法返回類型是URL類型。這樣纔可以動態生成。因爲Dubbo是所有配置信息都將轉換爲URL的參數。
例如com.alibaba.dubbo.rpc.Protocol接口:

@SPI("dubbo")
public interface Protocol {

    /**
     * 獲取缺省端口,當用戶沒有配置端口時使用。
     * 
     * @return 缺省端口
     */
    int getDefaultPort();

    /**
     * 暴露遠程服務:<br>
     * 1. 協議在接收請求時,應記錄請求來源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
     * 2. export()必須是冪等的,也就是暴露同一個URL的Invoker兩次,和暴露一次沒有區別。<br>
     * 3. export()傳入的Invoker由框架實現並傳入,協議不需要關心。<br>
     * 
     * @param <T> 服務的類型
     * @param invoker 服務的執行體
     * @return exporter 暴露服務的引用,用於取消暴露
     * @throws RpcException 當暴露服務出錯時拋出,比如端口已佔用
     */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    /**
     * 引用遠程服務:<br>
     * 1. 當用戶調用refer()所返回的Invoker對象的invoke()方法時,協議需相應執行同URL遠端export()傳入的Invoker對象的invoke()方法。<br>
     * 2. refer()返回的Invoker由協議實現,協議通常需要在此Invoker中發送遠程請求。<br>
     * 3. 當url中有設置check=false時,連接失敗不能拋出異常,並內部自動恢復。<br>
     * 
     * @param <T> 服務的類型
     * @param type 服務的類型
     * @param url 遠程服務的URL地址
     * @return invoker 服務的本地代理
     * @throws RpcException 當連接服務提供方失敗時拋出
     */
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    /**
     * 釋放協議:<br>
     * 1. 取消該協議所有已經暴露和引用的服務。<br>
     * 2. 釋放協議所佔用的所有資源,比如連接和端口。<br>
     * 3. 協議在釋放後,依然能暴露和引用新的服務。<br>
     */
    void destroy();

}

可以看出export方法和refer方法都有@Adaptive註解,refer方法中有URL類型的參數,export方法中雖然沒有URL參數,但是其參數Invoker中具有getUrl方法,返回類型爲URL類型。對於Protocol接口動態生成的Adaptive類代碼如下:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

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.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
        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);
    }

    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
        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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章