Dubbo採用微內核+插件模式的設計原則,微內核負責組裝插件,也就是Dubbo的全部功能點均可被用戶自定義擴展所替換。微內核是由Dubbo SPI機制實現的,所以瞭解Dubbo SPI是很是重要的。java
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源碼詳解。源碼分析
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 攜帶配置信息。