從兩個示例代碼桃犬,介紹dubbo的SPI的使用以及相關(guān)源碼分析恨豁,分析了獲取擴展實現(xiàn)和獲取自適應(yīng)擴展點實現(xiàn)的源碼沽瞭,最后簡單說了下ExtensionFactory的流程,看完就可以理解為什么dubbo是自包含的了豆同。從上往下看番刊,再回頭看,應(yīng)該能看明白影锈,文章比較長芹务,希望能耐心讀下去蝉绷。如果有錯誤的地方希望能指出來,我也理解不是太完整或者表述不是太明白锄禽。
ExtensionLoader使用以及簡單流程分析
假設(shè)有這樣一段示例代碼:
public static void main(String[] args) {
ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
Protocol dubboProtocol = extensionLoader.getExtension("dubbo");
System.out.println(dubboProtocol.getDefaultPort());
}
我們先通過ExtensionLoader.getExtensionLoader(Protocol.class)
獲取ExtensionLoader
實例潜必,然后通過getExtension("dubbo")
獲取到具體的Protocol
實現(xiàn)DubboProtocol
。
首先看下獲取ExtensionLoader
實例的過程:
- 各種校驗沃但。
- 從緩存中獲取指定類型的
ExtensionLoader
實例磁滚。 - 如果緩存中不存在的話,就新建一個
ExtensionLoader
實例宵晚,并放入緩存垂攘。 - 返回
ExtensionLoader
實例。
這部分源碼如下:
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 interface!");
}
// 沒有添加@SPI注解
if(!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
// 先從緩存中獲取指定類型的ExtensionLoader
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
// 緩存中不存在
if (loader == null) {
/**
* 創(chuàng)建一個新的ExtensionLoader實例淤刃,放到緩存中去
* 對于每一個擴展晒他,dubbo中只有個對應(yīng)的ExtensionLoader實例
*/
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
ExtensionLoader緩存
前面的校驗可以參考注釋,這里先說下緩存EXTENSION_LOADERS
:
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
可以看到每個SPI擴展的ExtensionLoader
的實例只有一個逸贾,緩存的key就是具體SPI接口類型陨仅,比如com.alibaba.dubbo.rpc.Protocol
作為key。
ExtensionLoader實例化
new ExtensionLoader<T>(type)
這里做了什么铝侵?
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (
type == ExtensionFactory.class ?
null :
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
);
}
上面示例代碼執(zhí)行后灼伤,第一次到這里,type是com.alibaba.dubbo.rpc.Protocol
咪鲜,所以這里會先執(zhí)行ExtensionLoader.getExtensionLoader(ExtensionFactory.class)
狐赡,然后執(zhí)行getAdaptiveExtension()
。
也就是說如果是第一次執(zhí)行獲取Protocol類型的ExtensionLoader的實例的話疟丙,會先獲取ExtensionFactory類型的ExtensionLoader實例颖侄。為什么要先獲取ExtensionFactory類型的ExtensionLoader的實例呢?因為ExtensionFactory是用來生成擴展點具體實現(xiàn)的工廠享郊,這里暫時先到這里览祖,后面會再說ExtensionFactory相關(guān)的東西。
獲取完了ExtensionFactory類型的ExtensionLoader后炊琉,緊接著調(diào)用getAdaptiveExtension()方法來獲取一個自適應(yīng)的ExtensionFactory實例穴墅,獲取自適應(yīng)AdaptiveExtensionFactory實例的原因是ExtensionFactory會有多個實現(xiàn),這樣可以在運行時來決定調(diào)用哪個具體實現(xiàn)温自,而不是直接寫死使用哪個具體實現(xiàn)。
ExtensionFactory的具體實現(xiàn)有三個:
- AdaptiveExtensionFactory
- SpiExtensionFactory
- SpringExtensionFactory
其中AdaptiveExtensionFactory注解了@Adaptive
注解皇钞,是ExtensionFactory這個SPI接口的自適應(yīng)實現(xiàn)悼泌,如果在運行時需要獲取一個ExtensionFactory的實現(xiàn)時,會調(diào)用AdaptiveExtensionFactory來進行動態(tài)獲取夹界。
說明一下馆里,一個擴展點最多只能有一個自適應(yīng)實現(xiàn),也就是一個擴展點的具體實現(xiàn)類最多只能有一個可以在類級別上注解@Adaptive
。如果一個擴展點沒有任何一個實現(xiàn)在類級別上注解@Adaptive
鸠踪,那么dubbo會在運行時動態(tài)生成一個自適應(yīng)實現(xiàn)類丙者,比如Protocol的具體實現(xiàn)類就沒有任何一個有在類級別上注解了@Adaptive
,dubbo會自動生成一個名字是Protocol$Adpative
的自適應(yīng)實現(xiàn)類营密。
使用ExtensionLoader獲取擴展點實現(xiàn)
上面的步驟完成了獲取Protocol類型的ExtensionLoader的實例械媒,同時也完成了ExtensionFactory類型的ExtensionLoader實例的加載,同時也生成了ExtensionFactory的自適應(yīng)實現(xiàn)评汰,接下來繼續(xù)往下走:
Protocol dubboProtocol = extensionLoader.getExtension("dubbo");
獲取了Protocol類型的ExtensionLoader實例后纷捞,就可以根據(jù)名字來加載具體的實現(xiàn)類了,Protocol的具體實現(xiàn)類有:
- DubboProtocol
- HessianProtocol
- HttpProtocol
- ThriftProtocol
- InjvmProtocol
- RmiProtocol
- WebServiceProtocol
- RegistryProtocol
- RedisProtocol
- MemcachedProtocol
- 一些Wrapper類
可以看到Protocol有很多具體的實現(xiàn)被去,根據(jù)使用協(xié)議的不同主儡,可以動態(tài)選擇具體使用哪一個Protocol實現(xiàn)。
繼續(xù)看getExtension()
方法:
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
// 獲取默認(rèn)實現(xiàn)
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);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 緩存不存在惨缆,創(chuàng)建實例
instance = createExtension(name);
// 加入緩存
holder.set(instance);
}
}
}
return (T) instance;
}
該方法是根據(jù)指定的名字來獲取具體的擴展點的實現(xiàn)的實例糜值,比如我們這里傳的name是dubbo,就會獲取DubboProtocol的實例坯墨,具體步驟如下:
- 校驗
- 如果name是true寂汇,就獲取默認(rèn)擴展點的實現(xiàn)實例
- 從緩存中獲取擴展點實現(xiàn)實例
- 如果緩存中不存在,就根據(jù)name創(chuàng)建具體的擴展點實現(xiàn)實例
- 返回name對應(yīng)的具體擴展點實現(xiàn)的實例
擴展點實現(xiàn)的實例緩存
獲取默認(rèn)擴展點實現(xiàn)實例暫時不說畅蹂,先看下cachedInstances緩存:
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
這里緩存了擴展點具體實現(xiàn)的實例健无,key是擴展點的名字,比如DubboProtocol的實例液斜,key就是dubbo累贤,value是DubboProtocol的實例,Holder中持有DubboProtocol的實例少漆。
創(chuàng)建擴展點實現(xiàn)實例
接下來看根據(jù)name創(chuàng)建具體擴展點實現(xiàn)實例的方法createExtension(name)
方法臼膏,該方法的代碼如下:
private T createExtension(String name) {
/**
* getExtensionClasses加載當(dāng)前擴展點的所有實現(xiàn)
* 比如:
* 我們在使用ExtensionLoader.getExtensionLoader(Protocol.class)
* 獲取Protocol的ExtensionLoader的時候,就已經(jīng)設(shè)置了當(dāng)前ExtensionLoader
* 的類型是Protocol的示损,所以這里獲取的時候就是Protocol的所有實現(xiàn)渗磅。
*
* 獲取到所有的實現(xiàn)之后,getExtensionClasses()返回的是Map<String, Class<?>>
*/
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
/**
* 從緩存中獲取已經(jīng)創(chuàng)建的擴展點的實現(xiàn)的實例
* 如果還沒有检访,就根據(jù)Class通過反射來創(chuàng)建具體的實例始鱼,
* 并放到緩存中去
*/
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
/**
* 向?qū)嵗凶⑷胍蕾嚨臄U展
* 如果一個擴展點A依賴了其他的擴展點B,并且有setter方法
* 就會執(zhí)行將擴展點B注入擴展點A的操作
*/
injectExtension(instance);
/**
* 如果擴展點有包裝類脆贵,將擴展點進行包裝
* 包裝后如果也依賴了其他擴展點医清,也需要注入其他擴展點
*/
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
該方法根據(jù)擴展點的名字來創(chuàng)建具體擴展點實現(xiàn)的實例,具體步驟如下:
- 通過
getExtensionClasses()
方法將當(dāng)前擴展點的所有的實現(xiàn)類進行加載卖氨,如果是@Adaptive
注解的自適應(yīng)實現(xiàn)類会烙,則放到cachedAdaptiveClass緩存中负懦;如果是包裝類,則放到cachedWrapperCalsses緩存中柏腻。經(jīng)過這一步纸厉,擴展點的所有實現(xiàn)都已經(jīng)解析加載。 - 根據(jù)名字獲取到具體的某一個擴展點實現(xiàn)類五嫂,并去EXTENSION_INSTANCES緩存中查詢是不是有實例颗品,如果沒有的話,就使用反射創(chuàng)建一個實例贫导。
- 如果該實例中依賴了其他的擴展點(需要有setter方法)抛猫,需要將依賴的擴展點進行注入。
- 如果擴展點有包裝類孩灯,則將擴展點進行包裝娩鹉,如果包裝后扛伍,也依賴了其他的擴展點(需要有setter方法)羡儿,需要將依賴的擴展點進行注入撮胧。
- 返回注入和包裝后的擴展點實現(xiàn)的實例,在我們的這個例子中返回的不是DubboProtocol實例了讥巡,而是經(jīng)過了ProtocolFilterWrapper和ProtocolListenerWrapper包裝后的實例掀亩。
總體的流程就算說完了,已經(jīng)獲取到了名字為dubbo的Protocol的實現(xiàn)的實例欢顷,接下來的執(zhí)行最后一行代碼槽棍,得到結(jié)果:
System.out.println(dubboProtocol.getDefaultPort());
加載擴展點實現(xiàn)類的Class
接下來我們看看getExtensionClasses()
方法具體做了什么,該方法是用來加載當(dāng)前擴展點的所有實現(xiàn)的class的抬驴,具體代碼如下:
private Map<String, Class<?>> getExtensionClasses() {
/**
* 先從緩存中獲取炼七,不存在的話就調(diào)用loadExtensionClasses進行加載
* cachedClasses緩存中存儲了當(dāng)前擴展點所有的實現(xiàn)類
*/
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
/**
* 如果沒有加載Extension的實現(xiàn),進行掃描加載布持,完成后緩存起來
* 每個擴展點豌拙,其實現(xiàn)的加載只會執(zhí)行一次
* 例如,如果Protocol的某個具體實現(xiàn)加載出錯了题暖,沒有放到緩存中去
* 后面再使用按傅,也不會再進行加載了。
*/
classes = loadExtensionClasses();
// 緩存起來
cachedClasses.set(classes);
}
}
}
return classes;
}
這里也只是嘗試從緩存中獲取胧卤,如果緩存中不存在的話唯绍,就進行具體的加載邏輯。但是這里有個點要注意枝誊,一個擴展點的的實現(xiàn)類加載只會執(zhí)行一次推捐。
繼續(xù)往下走就是真正的加載擴展點的實現(xiàn)邏輯了,代碼如下:
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) {
// 當(dāng)前擴展點的默認(rèn)實現(xiàn)名字侧啼,如果有的話進行緩存
String value = defaultAnnotation.value();
if(value != null && (value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if(names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if(names.length == 1) cachedDefaultName = names[0];
}
}
// 從配置文件中加載擴展實現(xiàn)類
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
// 從META-INF/dubbo/internal目錄下加載
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
// 從META-INF/dubbo/目錄下加載
loadFile(extensionClasses, DUBBO_DIRECTORY);
// 從META-INF/services/下加載
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
這里面邏輯也挺簡單的牛柒,先獲取擴展點的默認(rèn)名字,如果有的話進行緩存痊乾;然后就從配置文件中加載具體的實現(xiàn)類了皮壁,加載的位置有三個,請參照代碼里的注釋哪审。
具體的從配置文件中加載的代碼蛾魄,就不在貼出來了,太長了湿滓。說下大概的邏輯:
- 組裝配置文件名字滴须,加載配置文件,遍歷文件中每一行進行處理叽奥。
- 加載配置文件中配置的實現(xiàn)類扔水。
- 如果是注解了
@Adaptive
注解的實現(xiàn)類,加入到cachedAdaptiveClass緩存中朝氓。 - 如果是包裝類型的實現(xiàn)類魔市,加入到cachedWrapperClasses緩存中。
- 如果是除了上面兩種的類赵哲,放到extensionClasses這個map中待德,用于在上層返回。
擴展點依賴注入
我們在返回上面枫夺,還又一點沒說将宪,就是依賴注入的功能injectExtension的代碼:
private T injectExtension(T instance) {
try {
// 在獲取第一個擴展點的ExtensionLoader的實例的時候,objectFactory就被實例化了橡庞,是AdaptiveExtensionFactory
if (objectFactory != null) {
// 遍歷要注入的實例的方法
for (Method method : instance.getClass().getMethods()) {
// 只處理set方法较坛,比如setA,就是要把A注入到instance中
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
// set方法參數(shù)類型
Class<?> pt = method.getParameterTypes()[0];
try {
// setter方法對應(yīng)的屬性名毙死,也就是擴展點接口名稱
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
/**
* objectFactory是AdaptiveExtensionFactory實例
* 比如這里的pt是com.alibaba.dubbo.rpc.Protocol燎潮,property是protocol
* objectFactory就會根據(jù)這兩個參數(shù)去獲取Protocol對應(yīng)的擴展實現(xiàn)的實例
*/
Object object = objectFactory.getExtension(pt, property);
// 獲取到了setter方法的參數(shù)的實現(xiàn),可以進行注入
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
依賴注入的代碼也很簡單扼倘,就是實例化要注入的類确封,然后反射調(diào)用set方法注入實例中去。
自適應(yīng)擴展點使用
到這里再菊,使用指定名稱加載擴展點實現(xiàn)的流程就分析完了爪喘,但是這種直接指定擴展點名字的方式卻不是我們主要使用的方式【腊危可以想象一下秉剑,dubbo是可以配置多協(xié)議的,也就是可以同時配置比如dubbo稠诲、rmi等協(xié)議侦鹏。如果我們使用了多協(xié)議的話诡曙,那dubbo是怎么做的呢?我們可以想到最簡單的方法就是有一個轉(zhuǎn)發(fā)器略水,用來根據(jù)實際請求中配置的協(xié)議來使用不同的實現(xiàn)來處理价卤,下面可以寫個偽代碼:
public class ProtocolDispatcher implements Protocol {
public void refer(String name) {
if (name.equals("dubbo")) {
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
} else if(name.equals("rmi")) {
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rmi");
}
}
}
實際上dubbo中沒有這樣的代碼,但實際上也差不多類似這樣的方式來處理的渊涝,我們看下實際在dubbo中的使用方式:
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
可以看到第一步還是先獲取Protocol類型的ExtensionLoader的實例慎璧,這個過程跟最上面的獲取ExtensionLoader實例的過程是一樣的,接下來這一步getAdaptiveExtension()
就跟我們之前的示例不一樣了跨释,這是獲取自適應(yīng)擴展的方法胸私。
自適應(yīng)擴展是不是很熟悉,上面我們也說過自適應(yīng)鳖谈,可以回頭先去看下大概情況岁疼。首先說下獲取自適應(yīng)擴展是干嘛的?其實就是做到上面那個偽代碼的轉(zhuǎn)發(fā)器功能蚯姆。
自適應(yīng)擴展點動態(tài)生成的代碼
當(dāng)調(diào)用了上面getAdaptiveExtension()
方法后五续,dubbo會動態(tài)生成如下代碼:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
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.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);
}
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);
}
}
當(dāng)我們調(diào)用protocol.xxxx()
方法的時候,其實就是調(diào)用動態(tài)生成的Protocol$Adaptive
這個類的方法龄恋,這里面的邏輯其還是就跟我們的偽代碼差不多了疙驾,根據(jù)url中傳入的Protocol名字,通過getExtension(extName)
方法獲取實際的擴展點實現(xiàn)實例郭毕。
自適應(yīng)擴展點的獲取
接下來就看下獲取自適應(yīng)擴展的源碼:
public T getAdaptiveExtension() {
// 先從自適應(yīng)實例緩存中查找實例對象
Object instance = cachedAdaptiveInstance.get();
// 緩存中不存在
if (instance == null) {
if(createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
// 獲取鎖之后再檢查一次緩存中是不是已經(jīng)存在
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 緩存中沒有它碎,就創(chuàng)建新的AdaptiveExtension實例
instance = createAdaptiveExtension();
// 新實例加入緩存
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
}
else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
這邊還是老套路,先從緩存中獲取显押,如果緩存中不存在扳肛,就創(chuàng)建自適應(yīng)擴展實例,繼續(xù)看createAdaptiveExtension()
方法:
private T createAdaptiveExtension() {
try {
/**
* 先通過getAdaptiveExtensionClass獲取自適應(yīng)擴展類的Class
* 然后通過反射獲取實例
* 最后如果自適應(yīng)擴展依賴了其他的擴展點乘碑,就進行擴展點注入
*/
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
}
}
這里的邏輯跟createExtension()
差不多挖息,大概步驟:
- 先通過
getAdaptiveExtensionClass()
方法獲取自適應(yīng)擴展類的Class - 然后通過反射獲取實例
- 最后如果自適應(yīng)擴展類實例依賴了其他的擴展點,就進行擴展點的注入
獲取自適應(yīng)擴展點類的Class
首先看下獲取自適應(yīng)擴展類的Class方法:
private Class<?> getAdaptiveExtensionClass() {
/**
* getExtensionClasses加載當(dāng)前擴展點的所有實現(xiàn)
* 比如:
* 我們在使用ExtensionLoader.getExtensionLoader(Protocol.class)
* 獲取Protocol的ExtensionLoader的時候兽肤,就已經(jīng)設(shè)置了當(dāng)前ExtensionLoader
* 的類型是Protocol的套腹,所以這里獲取的時候就是Protocol的所有實現(xiàn)。
*
* 獲取到所有的實現(xiàn)之后资铡,getExtensionClasses()返回的是Map<String, Class<?>>
*
* 另外需要說的是电禀,如果擴展點的實現(xiàn)注解了類級別的@Adaptive注解,
* 這些實現(xiàn)的Class加載完后會賦值給cachedAdaptiveClass緩存笤休。如果擴展點的實現(xiàn)
* 是包裝類尖飞,這些實現(xiàn)的Class加載完后會放到cachedWrapperClasses緩存中。
* 其他的正常的擴展點的實現(xiàn)都會放到Map<String, Class<?>>中返回。
*
* 目前只有AdaptiveExtensionFactory和AdaptiveCompiler兩個實現(xiàn)類是被注解了@Adaptive
* 也就是說這兩個就是自適應(yīng)擴展政基,如果要加載ExtensionFactory和Compiler的自適應(yīng)擴展
* 不需要使用自動生成代碼贞铣,而是直接使用兩個實現(xiàn)類就可以了。
* 其他的擴展點如果想要獲取自適應(yīng)擴展實現(xiàn)沮明,就需要繼續(xù)往下走咕娄,使用生成的Xxx$Adaptive代碼。
*
* 一個擴展點有且只有一個自適應(yīng)擴展點珊擂,要么是內(nèi)置的兩個AdaptiveExtensionFactory和AdaptiveCompiler,
* 要么是生成的Xxx$Adaptive
*/
getExtensionClasses();
/**
* 自適應(yīng)擴展實現(xiàn)费变,在上面一步加載的時候摧扇,就會被加載緩存起來
* 只會執(zhí)行一次,后面再獲取的時候挚歧,就是獲取緩存起來的這個扛稽。
*/
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 沒有緩存自適應(yīng)擴展實現(xiàn),就動態(tài)創(chuàng)建一個
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
獲取自適應(yīng)擴展類的過程參考上面代碼的注釋即可滑负,繼續(xù)往下說創(chuàng)建自適應(yīng)擴展類的方法createAdaptiveExtensionClass()
:
private Class<?> createAdaptiveExtensionClass() {
/**
* 根據(jù)具體的接口來生成自適應(yīng)擴展類的代碼
* 比如Protocol就會生成Protocol$Adaptive為名字的類的代碼
*/
String code = createAdaptiveExtensionClassCode();
// 獲取類加載器
ClassLoader classLoader = findClassLoader();
// 獲取Compiler的自適應(yīng)擴展在张,獲取到的是AdaptiveCompiler實例
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 如果我們沒有指定名字,默認(rèn)使用javassist
return compiler.compile(code, classLoader);
}
這里大概的步驟是:
- 生成自適應(yīng)擴展類的代碼矮慕。
- 獲取類加載器帮匾。
- 獲取自適應(yīng)的Compiler的擴展實現(xiàn),獲取到的AdaptiveCompiler實例痴鳄,這個在上面已經(jīng)說過了瘟斜。
- 最后使用具體的Compiler進行生成代碼的編譯。
這里只看第一步痪寻,生成自適應(yīng)擴展類的代碼這步螺句,這里代碼有點長,不在此貼出來了橡类,參考我的github上ExtensionLoader的源碼注釋ExtensionLoader.java蛇尚。
@Adaptive注解
這里說下@Adaptive
注解,有兩種地方使用這個注解:
- 使用在實現(xiàn)類上
- 使用在接口的方法上
這兩種不能重復(fù)使用顾画。如果用在實現(xiàn)類上取劫,一個擴展點的實現(xiàn)類有且只能有一個類使用此注解,比如ExtensionFactory的實現(xiàn)類AdaptiveExtensionFactory使用了此注解亲雪,這個類本身就是一個自適應(yīng)擴展類了勇凭;如果用在接口的方法上,表示dubbo框架會在生成該接口的自適應(yīng)擴展類的時候义辕,生成該方法的代碼虾标,如果方法沒有添加此注解,則生成拋出不支持異常的代碼。
ExtensionFactory
到這里獲取擴展和獲取自適應(yīng)擴展就已經(jīng)說完了璧函,接下來可以把最上面留下的ExtensionFactory相關(guān)的加載流程說下了傀蚌,每個ExtensionLoader實例中都會有一個objectFactory實例,而objectFactory實例的賦值都是在ExtensionLoader的構(gòu)造方法中:
private ExtensionLoader(Class<?> type) {
this.type = type;
/**
* 對于擴展類型是ExtensionFactory的蘸吓,設(shè)置為null
* getAdaptiveExtension方法獲取一個運行時自適應(yīng)的擴展類型
* 每個Extension只能有一個@Adaptive類型的實現(xiàn)善炫,如果么有,dubbo會自動生成一個類
* objectFactory是一個ExtensionFactory類型的屬性库继,主要用于加載擴展的實現(xiàn)
*/
objectFactory = (
type == ExtensionFactory.class ?
null :
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
);
}
可以看到ExtensionFactory的實例獲取也是通過擴展點自適應(yīng)來獲取到的箩艺,獲取到的實例是AdaptiveExtensionFactory。而在AdaptiveExtensionFactory實例化的時候宪萄,會通過SPI機制加載所有的ExtensionFactory的實現(xiàn):
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
// 保存所有ExtensionFactor y的實現(xiàn)
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
使用objectFactory獲取擴展的時候艺谆,是調(diào)用AdaptiveExtensionFactory的getExtension方法,該方法會遍歷所有的ExtensionFactory的實現(xiàn)的getExtension方法:
public <T> T getExtension(Class<T> type, String name) {
// 依次遍歷各個ExtensionFactory實現(xiàn)的getExtension方法
// 找到Extension后立即返回拜英,沒找到返回null
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
共兩種實現(xiàn)SpiExtensionFactory和SpringExtensionFactory静汤,如果在任何一個實現(xiàn)中找到了擴展點實現(xiàn),就返回結(jié)束了居凶。
dubbo是自包含的虫给,這個概念通過上面的解析也應(yīng)該不難理解了。