版本
2.5.7
ExtensionLoader機制
Dubbo 的擴展點加載從 JDK 標準的 SPI (Service Provider Interface) 擴展點發現機制加強而來。
Dubbo 改進了 JDK 標準的 SPI 的以下問題:
- JDK 標準的 SPI 會?次性實例化擴展點所有實現,如果有擴展實現初始化很耗時,但如果沒用上也加載,會很浪費資源;
- 如果擴展點加載失敗,連擴展點的名稱都拿不到了。?如:JDK 標準的 ScriptEngine,通過getName()獲取腳本類型的名稱,但如果 RubyScriptEngine 因為所依賴的 jruby.jar 不存在,導致 RubyScriptEngine類加載失敗,這個失敗原因被吃掉了,和 ruby對應不起來,當用戶執行ruby 腳本時,會報不支持ruby,而不是真正失敗的原因;
- 增加了對擴展點 IoC 和 AOP 的支持,?個擴展點可以直接 setter 注?其它擴展點。
以上內容摘自官方文檔
ExtensionLoader的作用就是加載所有打上了@SPI
注解的接口,并根據配置進行實例化、封裝,包括dubbo自己的服務調用、暴露等功能,也是使用這種方式實現的。
加載過程
1、實例化ExtensionLoader
構造方法:
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
從代碼中可以看出,ExrtensionLoader
與SPI
是一一對應的關系,且每個封裝了SPI
的ExrtensionLoader
都保存了一個objectFactory
對象,而objectFactory
指向的都是ExrtensionLoader
的adaptive實現,即AdaptiveExtensionFactory
。
1.1 AdaptiveExtension實例化過程
getAdaptiveExtension
的實例化使用了互斥鎖,檢查了是否有實例化異常 的緩存后,通過createAdaptiveExtension()
方法實例化對象。
在實例化的過程中,會再次執行getExtensionClasses()
方法檢查是否已加載過配置文件。 以ExtensionFactory
舉例,因為在dubbo-common
的配置文件中,已經默認配置了ExtensionFactory
的兩個實現,且其AdaptiveExtensionFactory
上打了@Adaptive
注解,所以默認情況下會返回緩存中,第一次加載配置文件時初始化的AdaptiveExtensionFactory
類。
但如果緩存中沒有,比如Protocol
就沒有在實現類類上注解@Adaptive
,也沒關系,ExrtensionLoader
會通過createAdaptiveExtensionClass()
方法生成代碼,再編譯成class字節碼。
Protocol接口源碼
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
void destroy();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
}
Protocol動態生成的實現類:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive 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);
}
}
可以看出,打了@Adaotive
注解的方法,都會動態生成根據URL獲取信息的代碼,再組合成對應的實現類,并存儲到ExtensionLoader
的靜態變量中,以后就可以通過ExtensionLoader
加載對應的SPI
實現,然后執行業務邏輯。
2、獲取ExtensionLoader
名稱 | 配置位置 | getExtension方法實現 |
---|---|---|
SpiExtensionFactory | dubbo-common | 從ExtensionLoader 的靜態Map中讀取對象,若沒有,則通過new 關鍵字創建ExtensionLoader
|
SpringExtensionFactory | dubbo-config/dubbo-config-spring | 從Spring上下文中讀取對象 |
ExtensionFactory
也是一種SPI
,所有的SPI
都是通過ExtensionLoader
來封裝、加載的,每個被封裝的對象中都由ExtensionFactory objectFactory
屬性持有一個ExtensionFactory
的Adaptive實現AdaptiveExtensionFactory
,而AdaptiveExtensionFactory
又由List<ExtensionFactory> factories
持有兩個對象SpiExtensionFactory
及SpringExtensionFactory
。
他們的對比如下:
名稱 | 配置位置 | getExtension方法實現 |
---|---|---|
SpiExtensionFactory | dubbo-common | 從ExtensionLoader 的靜態Map中讀取對象,若沒有,則通過new 關鍵字創建ExtensionLoader
|
SpringExtensionFactory | dubbo-config/dubbo-config-spring | 從Spring上下文中讀取對象 |
當執行AdaptiveExtensionFactory
的getExtension()
方法時,它會輪詢二者之中有沒有要返回的對象,優先SPI的方式。
總結
通過ExtensionLoader
機制,dubbo封裝了所有SPI實現,并能夠使用@Adaptive
注解指定默認的實現,或者注解在方法上,動態將不同實現類的A\B\C方法組合成新的實現類,在靈活、可配、按需加載等方面做的非常出色。