Dubbo SPI擴展機制源碼詳解(基于2.7.10)
Dubbo SPI
一. 概述
本文主要分享 Dubbo 的拓展機制 SPI。
想要理解 Dubbo ,理解 Dubbo SPI 是非常必須的。在 Dubbo 中,提供了大量的拓展點,基于 Dubbo SPI 機制加載
Dubbo SPI官方文檔:Dubbo SPI 概述 | Apache Dubbo
本文基于 Dubbo 2.7.10 版本
二. Dubbo SPI特性
在看具體的 Dubbo SPI 實現之前,我們先理解 Dubbo SPI 產生的背景:
Dubbo 的擴展點加載從 JDK 標準的 SPI (Service Provider Interface) 擴展點發現機制加強而來。
Dubbo 改進了 JDK 標準的 SPI 的以下問題:
- JDK 標準的 SPI 會一次性實例化擴展點所有實現,如果有擴展實現初始化很耗時,但如果沒用上也加載,會很浪費資源。
- 如果擴展點加載失敗,連擴展點的名稱都拿不到了。比如:JDK 標準的 ScriptEngine,通過 getName() 獲取腳本類型的名稱,但如果 RubyScriptEngine 因為所依賴的 jruby.jar 不存在,導致 RubyScriptEngine 類加載失敗,這個失敗原因被吃掉了,和 ruby 對應不起來,當用戶執行 ruby 腳本時,會報不支持 ruby,而不是真正失敗的原因。
- 增加了對擴展點 IoC 和 AOP 的支持,一個擴展點可以直接 setter 注入其它擴展點。
用戶能夠基于 Dubbo 提供的擴展能力,很方便基于自身需求擴展其他協議、過濾器、路由等。下面介紹下 Dubbo 擴展能力的特性。
- 按需加載。Dubbo 的擴展能力不會一次性實例化所有實現,而是用那個擴展類則實例化那個擴展類,減少資源浪費。
- 增加擴展類的 IOC 能力。Dubbo 的擴展能力并不僅僅只是發現擴展服務實現類,而是在此基礎上更進一步,如果該擴展類的屬性依賴其他對象,則 Dubbo 會自動的完成該依賴對象的注入功能。
- 增加擴展類的 AOP 能力。Dubbo 擴展能力會自動的發現擴展類的包裝類,完成包裝類的構造,增強擴展類的功能。
- 具備動態選擇擴展實現的能力。Dubbo 擴展會基于參數,在運行時動態選擇對應的擴展類,提高了 Dubbo 的擴展能力。
- 可以對擴展實現進行排序。能夠基于用戶需求,指定擴展實現的執行順序。
- 提供擴展點的 Adaptive 能力。該能力可以使的一些擴展類在 consumer 端生效,一些擴展類在 provider 端生效。
從 Dubbo 擴展的設計目標可以看出,Dubbo 實現的一些例如動態選擇擴展實現、IOC、AOP 等特性,能夠為用戶提供非常靈活的擴展能力。
三. 代碼結構
Dubbo SPI 在 dubbo-common 的 org.apache.dubbo.common.extension 包實現,如下圖所示:

四. ExtensionLoader
org.apache.dubbo.common.extension.ExtensionLoader ,拓展加載器。這是 Dubbo SPI 的核心。
4.1 屬性
/**
* 拓展加載器集合
*/
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
/**
* 拓展加載器集合
* key:拓展接口
*/
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
/**
* 拓展接口
*/
private final Class<?> type;
/**
* 對象工廠
*
* 用于調用 {@link #injectExtension(Object)} 方法,向拓展對象注入依賴屬性。
*
* 例如,StubProxyFactoryWrapper 中有 `Protocol protocol` 屬性。
*/
private final ExtensionFactory objectFactory;
/**
* 緩存的拓展名與拓展類的映射。
*
* 和 {@link #cachedClasses} 的 KV 對調。
*
* 通過 {@link #loadExtensionClasses} 加載
*/
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
/**
* 緩存的拓展實現類集合。
*
* 不包含如下兩種類型:
* 1. 自適應拓展實現類。例如 AdaptiveExtensionFactory
* 2. 帶唯一參數為拓展接口的構造方法的實現類,或者說拓展 Wrapper 實現類。例如,ProtocolFilterWrapper 。
* 拓展 Wrapper 實現類,會添加到 {@link #cachedWrapperClasses} 中
*
* 通過 {@link #loadExtensionClasses} 加載
*/
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
/**
* 拓展名與 @Activate 的映射
*
* 例如,AccessLogFilter。
*
* 用于 {@link #getActivateExtension(URL, String)}
*/
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
/**
* 緩存的拓展對象集合
*
* key:拓展名
* value:拓展對象
*
* 例如,Protocol 拓展
* key:dubbo value:DubboProtocol
* key:injvm value:InjvmProtocol
*
* 通過 {@link #loadExtensionClasses} 加載
*/
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
/**
* 緩存的自適應( Adaptive )拓展對象
*/
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
/**
* 緩存的自適應拓展對象的類
*
* {@link #getAdaptiveExtensionClass()}
*/
private volatile Class<?> cachedAdaptiveClass = null;
/**
* 緩存的默認拓展名
*
* 通過 {@link SPI} 注解獲得
*/
private String cachedDefaultName;
/**
* 創建 {@link #cachedAdaptiveInstance} 時發生的異常。
*
* 發生異常后,不再創建,參見 {@link #createAdaptiveExtension()}
*/
private volatile Throwable createAdaptiveInstanceError;
/**
* 拓展 Wrapper 實現類集合
*
* 帶唯一參數為拓展接口的構造方法的實現類
*
* 通過 {@link #loadExtensionClasses} 加載
*/
private Set<Class<?>> cachedWrapperClasses;
/**
* 拓展名 與 加載對應拓展類發生的異常 的 映射
*
* key:拓展名
* value:異常
*
* 在 {@link #loadFile(Map, String)} 時,記錄
*/
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
我們將屬性分成了兩類:1)靜態屬性;2)對象屬性。這是為啥呢?
- 【靜態屬性】一方面,ExtensionLoader 是 ExtensionLoader 的管理容器。一個拓展( 拓展接口 )對應一個 ExtensionLoader 對象。例如,Protocol 和 Filter 分別對應一個 ExtensionLoader 對象。
- 【對象屬性】另一方面,一個拓展通過其 ExtensionLoader 對象,加載它的拓展實現們。我們會發現多個屬性都是 “cached” 開頭。ExtensionLoader 考慮到性能和資源的優化,讀取拓展配置后,會首先進行緩存。等到 Dubbo 代碼真正用到對應的拓展實現時,進行拓展實現的對象的初始化。并且,初始化完成后,也會進行緩存。也就是說:
- 緩存加載的拓展配置
- 緩存創建的拓展實現對象
4.2 獲得拓展配置
4.2.1 getExtensionClasses
/**
* 緩存的拓展實現類集合。
*
* 不包含如下兩種類型:
* 1. 自適應拓展實現類。例如 AdaptiveExtensionFactory
* 2. 帶唯一參數為拓展接口的構造方法的實現類,或者說拓展 Wrapper 實現類。例如,ProtocolFilterWrapper 。
* 拓展 Wrapper 實現類,會添加到 {@link #cachedWrapperClasses} 中
*
* 通過 {@link #loadExtensionClasses} 加載
*/
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
/**
* 緩存的自適應拓展對象的類
*
* {@link #getAdaptiveExtensionClass()}
*/
private volatile Class<?> cachedAdaptiveClass = null;
/**
* 拓展 Wrapper 實現類集合
*
* 帶唯一參數為拓展接口的構造方法的實現類
*
* 通過 {@link #loadExtensionClasses} 加載
*/
private Set<Class<?>> cachedWrapperClasses;
/**
* 獲得拓展實現
*
* @return 拓展實現類數組
*/
private Map<String, Class<?>> getExtensionClasses() {
// 從緩存中,獲得拓展實現類數組
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 從配置文件中,加載拓展實現類數組
classes = loadExtensionClasses();
// 設置到緩存中
cachedClasses.set(classes);
}
}
}
return classes;
}
- cachedClasses屬性,緩存的拓展實現類集合。它不包含如下兩種類型的拓展實現:
- 自適應拓展實現類。例如 AdaptiveExtensionFactory 。
- 拓展 Adaptive 實現類,會添加到
cachedAdaptiveClass屬性中。
- 拓展 Adaptive 實現類,會添加到
- 帶唯一參數為拓展接口的構造方法的實現類,或者說拓展 Wrapper 實現類。例如,ProtocolFilterWrapper 。
- 拓展 Wrapper 實現類,會添加到
cachedWrapperClasses屬性中。
- 拓展 Wrapper 實現類,會添加到
- 總結來說,
cachedClasses+cachedAdaptiveClass+cachedWrapperClasses才是完整緩存的拓展實現類的配置。
- 自適應拓展實現類。例如 AdaptiveExtensionFactory 。
4.2.2 loadExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
// 加載默認的拓展名
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
// 循環加載策略,加載對應的拓展實現
for (LoadingStrategy strategy : strategies) {
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
// 為了兼容 2.7 之前的老版本。在2.7之前,Dubbo還未進入Apache孵化,包名還是Alibaba
loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
}
return extensionClasses;
}
第一步:加載默認的擴展名
第二步:遍歷加載策略數組,去加載不同文件夾下的擴展。
strategies屬性是在啟動時,在 loadLoadingStrategies 方法中通過 Java SPI 加載的 LoadingStrategy 接口的實現類。
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
private static LoadingStrategy[] loadLoadingStrategies() {
return stream(load(LoadingStrategy.class).spliterator(), false)
.sorted()
.toArray(LoadingStrategy[]::new);
}
它會讀取 META-INF/services 下的 org.apache.dubbo.common.extension.LoadingStrategy 文件中注冊的實現類:

這些實現類中會指定,需要加載的拓展的配置文件路徑:
public class DubboInternalLoadingStrategy implements LoadingStrategy {
// 加載 META-INF/dubbo/internal/ 下的擴展實現
@Override
public String directory() {
return "META-INF/dubbo/internal/";
}
@Override
public int getPriority() {
return MAX_PRIORITY;
}
}

loadDirectory方法
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
// 獲得完整的文件名( 相對路徑 )。例如:"META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory" 。
String fileName = dir + type;// 第4行
try {
// 獲得文件名對應的所有文件數組
Enumeration<java.net.URL> urls = null;
ClassLoader classLoader = findClassLoader();
// try to load from ExtensionLoader's ClassLoader first
if (extensionLoaderClassLoaderFirst) {
ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
urls = extensionLoaderClassLoader.getResources(fileName);
}
}
if (urls == null || !urls.hasMoreElements()) { // 第 18 行
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
} // 第 24 行
if (urls != null) {
while (urls.hasMoreElements()) { // 第 27 行
java.net.URL resourceURL = urls.nextElement();
// 加載指定配置文件(resourceURL)下的實現
loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
}
} // 第 32 行
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}
-
第 4 行:獲得完整的文件名( 相對路徑 )。例如:
"META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory"。 -
第 18 至 24 行:獲得文件名對應的所有文件 URL 數組。例如:

-
第 27 至 32 行:遍歷逐個文件。
loadResource方法
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
try {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
String line;
String clazz = null;
while ((line = reader.readLine()) != null) {
// 去除 #注釋
final int ci = line.indexOf('#');
if (ci >= 0) {
line = line.substring(0, ci);
}
line = line.trim();
if (line.length() > 0) {
try {
// 拆分,key=value 的配置格式
String name = null;
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
clazz = line.substring(i + 1).trim();
} else {
// Dubbo SPI 會兼容 Java SPI 的配置格式,那么按照此處的解析方式,name 會為空。這種情況下,拓展名會自動生成
clazz = line;
}
if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) {
// 加載配置文件配置拓展的實現類
loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
- 第 7 行:進入文件內部,逐行遍歷。
loadClass方法
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
boolean overridden) throws NoSuchMethodException {
// 判斷拓展實現,是否實現拓展接口
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
// 檢測目標類上是否有 Adaptive 注解
if (clazz.isAnnotationPresent(Adaptive.class)) {
// 緩存自適應拓展對象的類到 `cachedAdaptiveClass`
cacheAdaptiveClass(clazz, overridden);
} else if (isWrapperClass(clazz)) {
// 檢測 clazz 是否是 Wrapper 類型
// 緩存拓展 Wrapper 實現類到 `cachedWrapperClasses`
cacheWrapperClass(clazz);
} else {
// 程序進入此分支,表明 clazz 是一個普通的拓展類
clazz.getConstructor();
// 未配置拓展名,自動生成。例如,DemoFilter 為 demo 。主要用于兼容 Java SPI 的配置。
if (StringUtils.isEmpty(name)) {
// 如果 name 為空,則嘗試從 Extension 注解中獲取 name,或使用小寫的類名作為 name
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
// 獲得拓展名,可以是數組,有多個拓展名。
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
// 緩存 @Activate 到 `cachedActivates` 。
cacheActivateClass(clazz, names[0]);
for (String n : names) {
// 緩存到 `cachedNames`
cacheName(clazz, n);
// 若 isWrapperClass 方法獲取構造方法失敗,則代表是普通的拓展實現類,緩存到 extensionClasses 變量中
saveInExtensionClass(extensionClasses, clazz, n, overridden);
}
}
}
}
4.3 獲得拓展加載器
在 Dubbo 的代碼里,常常能看到如下的代碼:
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)
4.3.1 getExtensionLoader
/**
* 根據拓展點的接口,獲得拓展加載器
*
* @param type 接口
* @param <T> 泛型
* @return 加載器
*/
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
// 必須是接口
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
// 必須包含 @SPI 注解
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
// 獲得接口對應的拓展點加載器
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
// 若不存在,則創建 ExtensionLoader 對象,并添加到 EXTENSION_LOADERS。
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
4.3.2 構造方法
構造方法,代碼如下:
/**
* 拓展接口。
* 例如,Protocol
*/
private final Class<?> type;
/**
* 對象工廠
*
* 用于調用 {@link #injectExtension(Object)} 方法,向拓展對象注入依賴屬性。
*
* 例如,StubProxyFactoryWrapper 中有 `Protocol protocol` 屬性。
*/
private final ExtensionFactory objectFactory;
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); // 第 17 行
}
objectFactory屬性,對象工廠,功能上和 Spring IOC 一致。- 用于調用
#injectExtension(instance)方法時,向創建的拓展注入其依賴的屬性。例如,CacheFilter.cacheFactory屬性。 - 第 17 行:當拓展接口非 ExtensionFactory 時(如果不加這個判斷,會是一個死循環),調用
ExtensionLoader#getAdaptiveExtension()方法,獲得 ExtensionFactory 拓展接口的自適應拓展實現對象。為什么呢?在 后文詳細解釋。
- 用于調用
4.4 獲得指定拓展對象
在 Dubbo 的代碼里,常常能看到如下的代碼:
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)
4.4.1 getExtension
/**
* 返回指定名字的擴展對象。如果指定名字的擴展不存在,則拋異常 {@link IllegalStateException}.
*
* @param name 拓展名
* @return 拓展對象
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {
return getExtension(name, true);
}
public T getExtension(String name, boolean wrap) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
// 查找 默認的 拓展對象
if ("true".equals(name)) {
return getDefaultExtension();
}
// 從 緩存中 獲得對應的拓展對象
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
// 從 緩存中 未獲取到,進行創建緩存對象。
if (instance == null) {
instance = createExtension(name, wrap);
// 設置創建對象到緩存中
holder.set(instance);
}
}
}
return (T) instance;
}
4.4.2 createExtension
#createExtension(name) 方法,創建拓展名的拓展對象,并緩存。代碼如下:
/**
* 創建拓展名的拓展對象,并緩存。
*
* @param name 拓展名
* @return 拓展對象
*/
@SuppressWarnings("unchecked")
private T createExtension(String name, boolean wrap) {
// 獲得拓展名對應的拓展實現類
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.getDeclaredConstructor().newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 注入依賴的屬性
injectExtension(instance);
if (wrap) {
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
// 創建 Wrapper 拓展對象
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);
}
}
Wrapper 類同樣實現了擴展點接口,但是 Wrapper 不是擴展點的真正實現。它的用途主要是用于從 ExtensionLoader 返回擴展點時,包裝在真正的擴展點實現外。即從 ExtensionLoader 中返回的實際上是 Wrapper 類的實例,Wrapper 持有了實際的擴展點實現類。
擴展點的 Wrapper 類可以有多個,也可以根據需要新增。
通過 Wrapper 類可以把所有擴展點公共邏輯移至 Wrapper 中。新加的 Wrapper 在所有的擴展點上添加了邏輯,有些類似 AOP,即 Wrapper 代理了擴展點。
- 例如:ListenerExporterWrapper、ProtocolFilterWrapper 。
4.4.3 injectExtension注入依賴
private T injectExtension(T instance) {
if (objectFactory == null) {
return instance;
}
try {
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 獲得屬性的類型
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// 獲取setter的屬性名,例如:setVersion,返回"version"
String property = getSetterProperty(method);
// 獲得屬性值
Object object = objectFactory.getExtension(pt, property); // 第 28 行
// 設置屬性值
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
- 第 28 行:獲得屬性值。注意,此處雖然調用的是
ExtensionFactory#getExtension(type, name)方法,實際獲取的不僅僅是拓展對象,也可以是 Spring Bean 對象。
4.5 獲得自適應的拓展對象
在 Dubbo 的代碼里,常常能看到如下的代碼:
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
友情提示,胖友先看下 「6. Adaptive」 的內容再回到此處。
Dubbo 自適應拓展的作用可以參考:SPI 自適應拓展 | Apache Dubbo
4.5.1 getAdaptiveExtension
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
// 從緩存中,獲得自適應拓展對象
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
// 若之前創建報錯,則拋出異常 IllegalStateException
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
}
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 創建自適應拓展對象
instance = createAdaptiveExtension();
// 設置到緩存
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
// 記錄異常
createAdaptiveInstanceError = t;
throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
}
return (T) instance;
}
4.5.2 createAdaptiveExtension
/**
* 創建自適應拓展對象
*
* @return 拓展對象
*/
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
try {
// 創建自適應拓展對象,并注入屬性
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
4.5.3 getAdaptiveExtensionClass
#getAdaptiveExtensionClass() 方法,獲得自適應拓展類。代碼如下:
/**
* @return 自適應拓展類
*/
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
// cachedAdaptiveClass 存在 直接返回
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 自動生成自適應拓展的代碼實現,并編譯后返回該類。
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
4.5.4 createAdaptiveExtensionClass
/**
* 自動生成自適應拓展的代碼實現,并編譯后返回該類。
*
* @return 類
*/
private Class<?> createAdaptiveExtensionClass() {
// 自動生成自適應拓展的代碼實現的字符串
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();// 第 5 行
// 編譯代碼,并返回該類
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
第 5 行會生成自適應拓展的代碼實現,然后會編譯字符串生成 Class。我們以 org.apache.dubbo.rpc.cluster.Cluster 接口為例,它生成的自適應拓展實現如下:
package org.apache.dubbo.rpc.cluster;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Cluster$Adaptive implements org.apache.dubbo.rpc.cluster.Cluster {
public org.apache.dubbo.rpc.cluster.Cluster getCluster(
java.lang.String arg0) {
throw new UnsupportedOperationException(
"The method public static org.apache.dubbo.rpc.cluster.Cluster org.apache.dubbo.rpc.cluster.Cluster.getCluster(java.lang.String) of interface org.apache.dubbo.rpc.cluster.Cluster is not adaptive method!");
}
public org.apache.dubbo.rpc.cluster.Cluster getCluster(
java.lang.String arg0, boolean arg1) {
throw new UnsupportedOperationException(
"The method public static org.apache.dubbo.rpc.cluster.Cluster org.apache.dubbo.rpc.cluster.Cluster.getCluster(java.lang.String,boolean) of interface org.apache.dubbo.rpc.cluster.Cluster is not adaptive method!");
}
public org.apache.dubbo.rpc.Invoker join(
org.apache.dubbo.rpc.cluster.Directory arg0)
throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) {
throw new IllegalArgumentException(
"org.apache.dubbo.rpc.cluster.Directory argument == null");
}
if (arg0.getUrl() == null) {
throw new IllegalArgumentException(
"org.apache.dubbo.rpc.cluster.Directory argument getUrl() == null");
}
// 獲取請求的URL
org.apache.dubbo.common.URL url = arg0.getUrl();
// 獲取URL上的 cluster 參數,如果沒有該參數,則默認為 failover
String extName = url.getParameter("cluster", "failover");
if (extName == null) {
throw new IllegalStateException(
"Failed to get extension (org.apache.dubbo.rpc.cluster.Cluster) name from url (" +
url.toString() + ") use keys([cluster])");
}
// 根據URL指定的集群容錯策略,加載對應的Cluster實現類,并調用對應實現的join方法
org.apache.dubbo.rpc.cluster.Cluster extension = (org.apache.dubbo.rpc.cluster.Cluster) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.cluster.Cluster.class)
.getExtension(extName);
return extension.join(arg0);
}
}
生成的代碼中,就是自適應拓展實現的核心,它會根據請求URL的參數,去動態加載對應的Cluster實現,完成不同的集群容錯策略。
4.6 獲得激活的拓展對象數組
在 Dubbo 的代碼里,看到使用代碼如下:
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
4.6.1 getActivateExtension
#getActivateExtension(url, key, group) 方法,獲得符合自動激活條件的拓展對象數組。
/**
* This is equivalent to {@code getActivateExtension(url, url.getParameter(key).split(","), null)}
* 獲得符合自動激活條件的拓展對象數組
* @param url url
* @param key url parameter key which used to get extension point names
* @param group group
* @return extension list which are activated.
* @see #getActivateExtension(org.apache.dubbo.common.URL, String[], String)
*/
public List<T> getActivateExtension(URL url, String key, String group) {
// 從 Dubbo URL 獲得參數值
String value = url.getParameter(key);
// 獲得符合自動激活條件的拓展對象數組
return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}
/**
* Get activate extensions.
* 獲得符合自動激活條件的拓展對象數組
* @param url url
* @param values extension point names
* @param group group
* @return extension list which are activated
* @see org.apache.dubbo.common.extension.Activate
*/
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> activateExtensions = new ArrayList<>();
List<String> names = values == null ? new ArrayList<>(0) : asList(values);
// 處理自動激活的拓展對象們
// 判斷不存在配置 `"-name"` 。例如,<dubbo:service filter="-default" /> ,代表移除所有默認過濾器。
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
// 獲得拓展實現類數組
getExtensionClasses();
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Object activate = entry.getValue();
String[] activateGroup, activateValue;
if (activate instanceof Activate) {
activateGroup = ((Activate) activate).group();
activateValue = ((Activate) activate).value();
} else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
} else {
continue;
}
if (isMatchGroup(group, activateGroup) // 匹配分組
&& !names.contains(name) // 不包含在自定義配置里。如果包含,會在下面的代碼處理。
&& !names.contains(REMOVE_VALUE_PREFIX + name) // 判斷是否配置移除。例如 <dubbo:service filter="-monitor" />,則 MonitorFilter 會被移除
&& isActive(activateValue, url)) { // 判斷是否激活
activateExtensions.add(getExtension(name));
}
}
// 排序
activateExtensions.sort(ActivateComparator.COMPARATOR);
}
// 處理自定義配置的拓展對象們。例如在 <dubbo:service filter="demo" /> ,代表需要加入 DemoFilter 。
List<T> loadedExtensions = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
// 將配置的自定義在自動激活的拓展對象們前面。例如,<dubbo:service filter="demo,default,demo2" /> ,則 DemoFilter 就會放在默認的過濾器前面。
if (DEFAULT_KEY.equals(name)) {
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(0, loadedExtensions);
loadedExtensions.clear();
}
} else {
// 獲得拓展對象
loadedExtensions.add(getExtension(name));
}
}
}
// 添加到結果集
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(loadedExtensions);
}
return activateExtensions;
}
五. @SPI
org.apache.dubbo.common.extension.SPI,擴展點接口的標識。代碼如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {
/**
* default extension name
*/
String value() default "";
}
-
value,默認拓展實現類的名字。例如,Protocol 拓展接口,代碼如下:@SPI("dubbo") public interface Protocol { // ... 省略代碼 }- 其中
"dubbo"指的是 DubboProtocol 做為 Protocol 默認的拓展實現類。
- 其中
六. @Adaptive
org.apache.dubbo.common.extension.Adaptive,自適應拓展信息的標記。代碼如下:
package org.apache.dubbo.common.extension;
import org.apache.dubbo.common.URL;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Provide helpful information for {@link ExtensionLoader} to inject dependency extension instance.
*
* @see ExtensionLoader
* @see URL
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
/**
* 從 {@link URL }的 Key 名,對應的 Value 作為要 Adapt 成的 Extension 名。
* <p>
* 如果 {@link URL} 這些 Key 都沒有 Value ,使用 缺省的擴展(注解 @SPI 設置的值)。<br>
* 比如,@Adptive({"key1", "key2"}),表示
* <ol>
* <li>先在URL上找key1的Value作為要Adapt成的Extension名;
* <li>key1沒有Value,則使用key2的Value作為要Adapt成的Extension名。
* <li>key2沒有Value,使用默認的擴展。
* <li>如果沒有設定缺省擴展,則方法調用會拋出{@link IllegalStateException}。
* </ol>
* <p>
* 如果參數名為空,則根據接口的類名生成一個默認的參數名,其規則是 接口名稱的全小寫。
* 例如 org.apache.dubbo.rpc.Protocol 接口,默認的參數名是:protocol
* 詳細邏輯參考:org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator#getMethodAdaptiveValue
*
* @see SPI#value()
*/
String[] value() default {};
}
@Adaptive注解,可添加類或方法上,分別代表了兩種不同的使用方式。
友情提示:一個拓展接口,有且僅有一個 Adaptive 拓展實現類。
- 第一種,標記在類上,代表手動實現(代碼中聲明一個類)它是一個拓展接口的 Adaptive 拓展實現類。目前 Dubbo 項目里,只有 ExtensionFactory 拓展的實現類 AdaptiveExtensionFactory 有這么用。
- 第二種,標記在拓展接口的方法上,代表自動生成代碼實現該接口的 Adaptive 拓展實現類(參考:[「4.5.4 createAdaptiveExtensionClassCode」](# 4.5.4 createAdaptiveExtensionClass))。
- value,從 Dubbo URL 獲取參數中,使用鍵名(Key),獲取鍵值。該值為真正的拓展名。
- 自適應拓展實現類,會獲取拓展名對應的真正的拓展對象。通過該對象,執行真正的邏輯。
- 可以設置多個鍵名(Key),順序獲取直到有值。若最終獲取不到,使用默認拓展名。
- 在 「4.5.4 createAdaptiveExtensionClassCode」 詳細解析。
- value,從 Dubbo URL 獲取參數中,使用鍵名(Key),獲取鍵值。該值為真正的拓展名。
七. @Activate
org.apache.dubbo.common.extension.Activate,自動激活條件的標記。代碼如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
/**
* Activate the current extension when one of the groups matches. The group passed into
* {@link ExtensionLoader#getActivateExtension(URL, String, String)} will be used for matching.
*
* @return group names to match
* @see ExtensionLoader#getActivateExtension(URL, String, String)
*/
/**
* Group過濾條件。
* <br />
* 包含{@link ExtensionLoader#getActivateExtension}的group參數給的值,則返回擴展。
* <br />
* 如沒有Group設置,則不過濾。
*/
String[] group() default {};
/**
* Activate the current extension when the specified keys appear in the URL's parameters.
* <p>
* For example, given <code>@Activate("cache, validation")</code>, the current extension will be return only when
* there's either <code>cache</code> or <code>validation</code> key appeared in the URL's parameters.
* </p>
*
* @return URL parameter keys
* @see ExtensionLoader#getActivateExtension(URL, String)
* @see ExtensionLoader#getActivateExtension(URL, String, String)
*/
/**
* Key過濾條件。包含{@link ExtensionLoader#getActivateExtension}的URL的參數Key中有,則返回擴展。
* <p/>
* 示例:<br/>
* 注解的值 <code>@Activate("cache,validatioin")</code>,
* 則{@link ExtensionLoader#getActivateExtension}的URL的參數有<code>cache</code>Key,或是<code>validatioin</code>則返回擴展。
* <br/>
* 如沒有設置,則不過濾。
*/
String[] value() default {};
/**
* Relative ordering info, optional
* Deprecated since 2.7.0
*
* @return extension list which should be put before the current one
*/
/**
* 排序信息,可以不提供。
*/
@Deprecated
String[] before() default {};
/**
* Relative ordering info, optional
* Deprecated since 2.7.0
*
* @return extension list which should be put after the current one
*/
/**
* 排序信息,可以不提供。
*/
@Deprecated
String[] after() default {};
/**
* Absolute ordering info, optional
*
* @return absolute ordering info
*/
/**
* 排序信息,可以不提供。
*/
int order() default 0;
}
- 對于可以被框架中自動激活加載擴展,
@Activate用于配置擴展被自動激活加載條件。比如,Filter 擴展,有多個實現,使用@Activate的擴展可以根據條件被自動加載。 - 分成過濾條件和排序信息兩類屬性,大家可以看下代碼里的注釋。
- 在 [「4.6 獲得激活的拓展對象數組」](#4.6 獲得激活的拓展對象數組) 詳細解析。
八. ExtensionFactory
org.apache.dubbo.common.extension.ExtensionFactory,拓展工廠接口。代碼如下:
/**
* ExtensionFactory
*
* 拓展工廠接口
*/
@SPI
public interface ExtensionFactory {
/**
* Get extension.
*
* 獲得拓展對象
*
* @param type object type. 拓展接口
* @param name object name. 拓展名
* @return object instance. 拓展對象
*/
<T> T getExtension(Class<T> type, String name);
}
- ExtensionFactory 自身也是拓展接口,基于 Dubbo SPI 加載具體拓展實現類。
#getExtension(type, name)方法,在 [「4.4.3 injectExtension」](#4.4.3 injectExtension) 中,獲得拓展對象,向創建的拓展對象注入依賴屬性。在實際代碼中,我們可以看到不僅僅獲得的是拓展對象,也可以是 Spring 中的 Bean 對象。- ExtensionFactory 子類類圖如下:

8.1 AdaptiveExtensionFactory
org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory,自適應 ExtensionFactory 拓展實現類。代碼如下:
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
/**
* ExtensionFactory 拓展對象集合
*/
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
// 使用 ExtensionLoader 加載拓展對象實現類。
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
// 遍歷工廠數組,直到獲得到屬性
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
@Adaptive注解,為 ExtensionFactory 的自適應拓展實現類。- 構造方法,使用 ExtensionLoader 加載 ExtensionFactory 拓展對象的實現類。若胖友沒自己實現 ExtensionFactory 的情況下,
factories為 SpiExtensionFactory 和 SpringExtensionFactory 。 #getExtension(type, name)方法,遍歷factories,調用其#getExtension(type, name)方法,直到獲得到屬性值。
8.2 SpiExtensionFactory
org.apache.dubbo.common.extension.factory.SpiExtensionFactory,SPI ExtensionFactory 拓展實現類。代碼如下:
public class SpiExtensionFactory implements ExtensionFactory {
/**
* 獲得拓展對象
*
* @param type object type. 拓展接口
* @param name object name. 拓展名
* @param <T> 泛型
* @return 拓展對象
*/
@Override
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {// 校驗是 @SPI
// 加載拓展接口對應的 ExtensionLoader 對象
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
// 加載拓展對象
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
}
8.3 SpringExtensionFactory
org.apache.dubbo.config.spring.extension.SpringExtensionFactory,Spring ExtensionFactory 拓展實現類。代碼如下:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.config.spring.extension;
import org.apache.dubbo.common.extension.ExtensionFactory;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.Set;
import static org.apache.dubbo.config.spring.util.DubboBeanUtils.getOptionalBean;
/**
* SpringExtensionFactory
*/
public class SpringExtensionFactory implements ExtensionFactory {
private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);
/**
* Spring Context 集合
*/
private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();
public static void addApplicationContext(ApplicationContext context) {
CONTEXTS.add(context);
if (context instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) context).registerShutdownHook();
}
}
public static void removeApplicationContext(ApplicationContext context) {
CONTEXTS.remove(context);
}
public static Set<ApplicationContext> getContexts() {
return CONTEXTS;
}
// currently for test purpose
public static void clearContexts() {
CONTEXTS.clear();
}
@Override
@SuppressWarnings("unchecked")
public <T> T getExtension(Class<T> type, String name) {
//SPI should be get from SpiExtensionFactory
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
return null;
}
for (ApplicationContext context : CONTEXTS) {
// 獲得屬性
T bean = getOptionalBean(context, name, type);
if (bean != null) {
return bean;
}
}
//logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
return null;
}
}
例子:
DemoFilter 是筆者實現的 Filter 拓展實現類,代碼如下:
public class DemoFilter implements Filter {
private DemoDAO demoDAO;
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
return invoker.invoke(invocation);
}
public DemoFilter setDemoDAO(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
return this;
}
}
-
DemoDAO ,筆者在 Spring 中聲明對應的 Bean 對象。
<bean id="demoDAO" class="com.alibaba.dubbo.demo.provider.DemoDAO" /> -
在 「4.4.3 injectExtension」 中,會調用
#setDemoDAO(demo)方法,將 DemoFilter 依賴的屬性demoDAO注入。
本文參考至:

浙公網安備 33010602011771號