天天看點

dubbo 之 ExtensionLoader

dubbo 通過 SPI + ExtensionLoader 實作了一種比較靈活的定制開發的方式,SPI 即配置檔案,檔案名為接口類的全名,内容為 "key=接口實作類的全名",而 ExtensionLoader 是一個類似于 spring 的容器,在 dubbo 項目中需要擷取一個類的執行個體時,直接調用 ExtensionLoader 的 api 擷取,同時它也會為對象注入屬性。

用下面代碼,描述擷取一個 SPI 接口的執行個體,首先指定接口名擷取 ExtensionLoader,然後通過 ExtensionLoader 指定擴充名擷取接口的實作類執行個體。

// org.apache.dubbo.config.ReferenceConfig#postProcessAfterScopeModelChanged
@Override
protected void postProcessAfterScopeModelChanged() {
  super.postProcessAfterScopeModelChanged();
  protocolSPI = this.getExtensionLoader(Protocol.class).getAdaptiveExtension();
  proxyFactory = this.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
}

// org.apache.dubbo.config.ReferenceConfig#createInvokerForRemote
invoker = protocolSPI.refer(interfaceClass, urls.get(0));      

接口通過 @SPI 注解聲明為 SPI 接口,同時注解中指定了預設的擴充名。

而接口方法則通過 @Adaptive 注解聲明為 adaptive 方法,adaptive 方法從 @Adaptive 中擷取擴充名的 key,如果沒有指定 key,則 key 為接口名的首字母小寫以句号連接配接,知道了 key,則在 URL 中獲得 key 對應的擴充名。

@SPI(value = "dubbo", scope = ExtensionScope.FRAMEWORK)
public interface Protocol {

    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    // 注解沒有指定 key 名,則 key 為 protocol,Protocol$Adaptive 類從 URL 的 protocol 參數擷取擴充名,然後通過
    // 擴充名擷取擴充
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

}      
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {

    public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.apache.dubbo.rpc.Protocol.class);
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) scopeModel.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }

}      
// 通過接口名,加載 SPI 的配置檔案,加載該接口對應的所有實作類對象
public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type)

// 擷取接口的 Adaptive 類對象,在 Adaptive 類中通過 URL 的參數傳遞擴充名
public T getAdaptiveExtension()

// 指定擴充名擷取指定接口實作類的對象
public T getExtension(String name)

public T getExtension(String name, boolean wrap)