Dubbo 沒使用 Java SPI,而重新實作了一套功能更強的 SPI。
Dubbo SPI 邏輯封裝在 ExtensionLoader 類,通過 ExtensionLoader,可加載指定實作類。Dubbo SPI 所需配置檔案需放置在
META-

INF/dubbo
路徑
配置内容如下:
Protocol protocol = ExtensionLoader
.getExtensionLoader(Protocol.class)
.getAdaptiveExtension();
Dubbo要判斷系統運作時,應該選用該Protocol接口的哪個實作類。
它會去找一個你配置的Protocol,将你配置的Protocol實作類,加載進JVM,将其執行個體化。
微核心,可插拔,大量的元件,Protocol負責RPC調用的東西,你可以實作自己的RPC調用元件,實作Protocol接口,給自己的一個實作類即可。
這行代碼就是Dubbo裡大量使用的,對很多元件都保留一個接口和多個實作,然後在系統運作的時候動态根據配置去找到對應實作類。
若你沒配置,那就走預設實作。
執行個體
在Dubbo自己的jar裡
在
/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
檔案中:
即可看到Dubbo的SPI機制預設流程,就是Protocol接口
- @SPI(“dubbo”)
通過SPI機制提供實作類,實作類是通過将dubbo作為預設key去配置檔案裡找到的,配置檔案名稱為接口全限定名,通過dubbo作為key可以找到預設的實作類org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
Dubbo的預設網絡通信協定,就是dubbo協定,用的DubboProtocol
在 Java 的 SPI 配置檔案裡每一行隻有一個實作類的全限定名,在 Dubbo的 SPI配置檔案中是 key=value 的形式,我們隻需要對應的 key 就能加載對應的實作。
源碼
/**
* 傳回指定名字的擴充。如果指定名字的擴充不存在,則抛異常 {@link IllegalStateException}.
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
// DCL(double check lock)
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
不用像 Java 原生的 SPI 那樣去周遊加載對應的服務類,隻需要通過 key 去尋找,并且尋找的時候會先從緩存的對象裡去取。
private T createExtension(String name) {
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
若想動态替換預設實作類,需使用@Adaptive接口。Protocol接口中,有兩個方法添加了@Adaptive注解,就是說那倆接口會被代理實作。
比如這個Protocol接口搞了倆@Adaptive注解了方法,在運作時會針對Protocol生成代理類,該代理類的那倆方法中會有代理代碼,代理代碼會在運作時動态根據url中的protocol來擷取key(預設是dubbo),也可以自己指定,如果指定了别的key,那麼就會擷取别的實作類的執行個體。通過這個url中的參數不同,就可以控制動态使用不同的元件實作類
擴充Dubbo元件
自己寫個工程,可以打成jar包的那種哦
- 裡面的
目錄下src/main/resources
- 搞一個
META-INF/services
- 裡面放個檔案叫:
com.alibaba.dubbo.rpc.Protocol
- 檔案裡搞一個
my=com.javaedge.MyProtocol
- 自己把jar弄到nexus私服
- 然後自己搞一個dubbo provider工程,在這個工程裡面依賴你自己搞的那個jar
- 然後在spring配置檔案裡給個配置:
<dubbo:protocol name=”my” port=”20000” />
這個時候provider啟動的時候,就會加載到我們jar包裡的my=com.javaedge.MyProtocol這行配置,接着會根據你的配置使用你定義好的MyProtocol了,這個就是簡單說明一下,你通過上述方式,可以替換掉大量的dubbo内部的元件,就是扔個你自己的jar包,然後配置一下即可~
Dubbo的SPI原理圖
Dubbo中提供了大量的類似上面的擴充點。
你要擴充一個東西,隻需自己寫個jar,讓你的consumer或者是provider工程,依賴它,在你的jar裡指定目錄下配置好接口名稱對應的檔案,裡面通過key=實作類然後對對應的元件,用類似<dubbo:protocol>用你的哪個key對應的實作類來實作某個接口,你可以自己去擴充dubbo的各種功能,提供你自己的實作!