title: Dubbo SPI機(jī)制分析
tags: Dubbo,SPI,源碼
grammar_cjkRuby: true
Dubbo 的擴(kuò)展點(diǎn)加載從 JDK 標(biāo)準(zhǔn)的 SPI (Service Provider Interface) 擴(kuò)展點(diǎn)發(fā)現(xiàn)機(jī)制加強(qiáng)而來(lái)晚碾。 Dubbo 改進(jìn)了 JDK 標(biāo)準(zhǔn)的 SPI 的以下問(wèn)題:
- JDK 標(biāo)準(zhǔn)的 SPI 會(huì)一次性實(shí)例化擴(kuò)展點(diǎn)所有實(shí)現(xiàn),如果有擴(kuò)展實(shí)現(xiàn)初始化很耗時(shí),但如果沒(méi)用上也加載假瞬,會(huì)很浪費(fèi)資源。
- 如果擴(kuò)展點(diǎn)加載失敗构回,連擴(kuò)展點(diǎn)的名稱都拿不到了嘉熊。比如:JDK 標(biāo)準(zhǔn)的 ScriptEngine遥赚,通過(guò) getName() 獲取腳本類型的名稱,但如果 RubyScriptEngine 因?yàn)樗蕾嚨?jruby.jar 不存在阐肤,導(dǎo)致 RubyScriptEngine 類加載失敗凫佛,這個(gè)失敗原因被吃掉了,和 ruby 對(duì)應(yīng)不起來(lái)孕惜,當(dāng)用戶執(zhí)行 ruby 腳本時(shí)愧薛,會(huì)報(bào)不支持 ruby,而不是真正失敗的原因衫画。
- 增加了對(duì)擴(kuò)展點(diǎn) IoC 和 AOP 的支持毫炉,一個(gè)擴(kuò)展點(diǎn)可以直接 setter 注入其它擴(kuò)展點(diǎn)。
源碼分析
dubbo的Extension基本架構(gòu)如圖所示:
這里最重要的類就是ExtensionLoader
.
獲取動(dòng)態(tài)自適應(yīng)拓展實(shí)現(xiàn)類
首先分析
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()
getAdaptiveExtension()
首先分析此方法:
public T getAdaptiveExtension() {
//1削罩、先從自適應(yīng)實(shí)例緩存中獲取實(shí)例
Object instance = cachedAdaptiveInstance.get();
//2瞄勾、雙重鎖檢查,如果實(shí)例不存在鲸郊,則生成實(shí)例
if (instance == null) {
if (createAdaptiveInstanceError == null) {
//cachedAdaptiveInstance存放Adaptive修飾類的實(shí)例
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//3丰榴、生成實(shí)例
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;
}
- 首先從自適應(yīng)實(shí)例緩存
cachedAdaptiveInstance
中獲取@Adaptive
修飾的類 - 如果實(shí)例為空,則進(jìn)行雙重鎖檢查模式創(chuàng)建一個(gè)自適應(yīng)適配器拓展點(diǎn)
createAdaptiveExtension
private T createAdaptiveExtension() {
try {
//1秆撮、先獲取自適應(yīng)拓展點(diǎn)實(shí)現(xiàn)類四濒,然后實(shí)例化
//2、注入
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create ad" +
"aptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
- 首先通過(guò)
getAdaptiveExtensionClass()
獲取對(duì)應(yīng)的class - 實(shí)例化
- 注入
getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
//加載當(dāng)前拓展點(diǎn)的所有實(shí)現(xiàn)职辨,如果有被@Adaptive修飾的實(shí)現(xiàn)類盗蟆,則緩存在cachedAdaptiveClass
getExtensionClasses();
//判斷是否有被@Adaptive修飾的實(shí)現(xiàn)類,有的話則返回
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//沒(méi)有@Adaptive修飾的實(shí)現(xiàn)類舒裤,創(chuàng)建動(dòng)態(tài)自適應(yīng)拓展點(diǎn)實(shí)現(xiàn)
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
- 通過(guò)
getExtensionClasses
加載當(dāng)前拓展點(diǎn)的所有實(shí)現(xiàn) - 如果有
@Adaptive
修飾的實(shí)現(xiàn)類喳资,則緩存在cachedAdaptiveClass
,并返回 - 如果沒(méi)有
@Adaptive
修飾的實(shí)現(xiàn)類腾供,則通過(guò)createAdaptiveExtensionClass
創(chuàng)建動(dòng)態(tài)自適應(yīng)拓展點(diǎn)實(shí)現(xiàn)
getExtensionClasses
首先分析getExtensionClasses
:
private Map<String, Class<?>> getExtensionClasses() {
//判斷拓展點(diǎn)實(shí)現(xiàn)類緩存cachedClasses是否為空仆邓,如果為空,通過(guò)雙重所檢查模式進(jìn)行拓展點(diǎn)實(shí)現(xiàn)類加載
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//加載拓展點(diǎn)實(shí)現(xiàn)類
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
判斷拓展點(diǎn)實(shí)現(xiàn)類緩存cachedClasses
是否為空伴鳖,如果為空节值,通過(guò)雙重鎖檢查模式進(jìn)行拓展點(diǎn)實(shí)現(xiàn)類加載loadExtensionClasses
loadExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
//判斷當(dāng)前類型是否是可拓展點(diǎn)且獲取默認(rèn)拓展實(shí)現(xiàn)類
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
//每個(gè)拓展接口只能有一個(gè)拓展實(shí)現(xiàn)默認(rèn)名,如Protocol的默認(rèn)實(shí)現(xiàn)是“dubbo”
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));
}
//設(shè)置默認(rèn)名榜聂,如Protocol的為dubbo
if (names.length == 1) cachedDefaultName = names[0];
}
}
//根據(jù)傳入類型搞疗,對(duì)【META-INF/dubbo/internal/】 【META-INF/dubbo/】【 META-INF/services/】
//進(jìn)行拓展點(diǎn)實(shí)現(xiàn)類的加載
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
return extensionClasses;
}
loadExtensionClasses()
做了這么幾件事:
- 判斷當(dāng)前類型是否是拓展點(diǎn)接口,即傳入類型是否由@SPI修飾
- 判斷SPI的name的個(gè)數(shù)须肆,如果大于1匿乃,則拋錯(cuò)桩皿;否則獲取name的值,然后將它設(shè)置為默認(rèn)值
cachedDefaultName
- 對(duì)
META-INF/dubbo/internal/
META-INF/dubbo/
META-INF/services/
進(jìn)行拓展點(diǎn)實(shí)現(xiàn)類的加載
loadDirectory
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
//拼接文件名幢炸,如META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls;
ClassLoader classLoader = findClassLoader();
//加載classpath下所有對(duì)應(yīng)type的文件泄隔,如META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol,然后進(jìn)行合并加載
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
//遍歷所有對(duì)應(yīng)type的文件阳懂,如org.apache.dubbo.rpc.Protocol梅尤,然后加載class
while (urls.hasMoreElements()) {
//獲取絕對(duì)路徑
java.net.URL resourceURL = urls.nextElement();
//獲取jar下對(duì)應(yīng)文件的class
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
- 將對(duì)應(yīng)的文檔以及類型全路徑進(jìn)行拼接,如
META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
- 遍歷classpath對(duì)應(yīng)路徑下的文件岩调,然后掃描加載
進(jìn)入loadResource
,讀取文件內(nèi)容
loadResource
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
String line;
//輪流讀取行數(shù)據(jù)赡盘,獲取拓展接口實(shí)現(xiàn)類
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 {
String name = null;
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
//循環(huán)加載拓展接口實(shí)現(xiàn)類
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
}
} 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);
}
}
}
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
這個(gè)方法做的事情很簡(jiǎn)單:
- 按行讀取文件里面的內(nèi)容
- 將每一行的內(nèi)容截取到“#”為止号枕,然后以“=”為分隔符,取等號(hào)前面的內(nèi)容為
name
陨享,等號(hào)后面的內(nèi)容為拓展點(diǎn)實(shí)現(xiàn)類的全路徑 - 根據(jù)
name
和拓展點(diǎn)實(shí)現(xiàn)類全路徑葱淳,加載class
loadClass
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
//判斷是否實(shí)現(xiàn)了類型接口
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error when load extension class(interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + "is not subtype of interface.");
}
//如果是自定義的適配拓展實(shí)現(xiàn)類,則設(shè)置cachedAdaptiveClass抛姑,并返回
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
//如果是包裝類的話赞厕,走包裝類的邏輯,即加載存儲(chǔ)在cachedWrapperClasses:ProtocolFilterWrapper定硝,ProtocolListenerWrapper
} else if (isWrapperClass(clazz)) {
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} else {
//如果沒(méi)有指定自定義的適配拓展實(shí)現(xiàn)類皿桑,且沒(méi)有包裝類,說(shuō)明是普通的拓展點(diǎn)實(shí)現(xiàn)類
clazz.getConstructor();
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
//分割name蔬啡,獲取name數(shù)組
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
//判斷是否被@Active修飾的拓展點(diǎn)實(shí)現(xiàn)類诲侮,如果是,則使用cachedActivates緩存
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
} else {
// support com.alibaba.dubbo.common.extension.Activate
com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
if (oldActivate != null) {
cachedActivates.put(names[0], oldActivate);
}
}
//同一個(gè)實(shí)現(xiàn)類對(duì)應(yīng)多個(gè)name箱蟆,以name=>class形式存放于extensionClasses沟绪,adaptive修飾的不會(huì)存放,包裝類不會(huì)存放
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
這個(gè)方法做了這么幾件事:
攔截沒(méi)有實(shí)現(xiàn)指定類型的拓展點(diǎn)實(shí)現(xiàn)類
如果傳入
class
是由@Adaptive
修飾空猜,那么使用cachedAdaptiveClass
緩存绽慈,只能緩存一個(gè)-
如果是包裝類,符合以下條件
clazz.getConstructor(type)
,使用Set
(cachedWrapperClasses
)存儲(chǔ)
除了兩種類型之外辈毯,剩下的就是普通的拓展點(diǎn)實(shí)現(xiàn)類坝疼,判斷是否由
@Active
的自動(dòng)激活的類型,如果是漓摩,使用Map
緩存cachedActivates
以name->Active的形式存儲(chǔ)裙士;然后分割name
得到對(duì)應(yīng)的name數(shù)組
,遍歷數(shù)組,使用cachedNames
緩存以class->name
的形式存儲(chǔ)管毙,同時(shí)腿椎,以name->class
的形式存儲(chǔ)在緩存cachedClasses
中
至此桌硫,getExtensionClasses
的邏輯走完,梳理下它的邏輯:
1啃炸、判斷拓展點(diǎn)實(shí)現(xiàn)類的緩存cachedClasses
是否為空铆隘,如果為空,進(jìn)行加載操作
2南用、根據(jù)傳入類型膀钠,獲取SPI注解的值,然后設(shè)置默認(rèn)名cachedDefaultNam
3裹虫、在META-INF/dubbo/internal/
META-INF/dubbo/
META-INF/services/
這三個(gè)目錄下進(jìn)行拓展點(diǎn)加載
4肿嘲、遍歷文件(文件名為傳入類型type
的全路徑名稱)內(nèi)容,然后根據(jù)name->className
加載class
:
- 如果是
@Adaptive
修飾筑公,那么將當(dāng)前class
緩存在cachedAdaptiveClass
,該值是單例 - 如果是包裝類雳窟,如
ProtocolFilterWrapper
,ProtocolListenerWrapper
匣屡,則將class
緩存在set
集合cachedWrapperClasses
中 - 如果是普通的拓展點(diǎn)實(shí)現(xiàn)類
- 如果當(dāng)前類是
@Active
修飾的封救,那么使用緩存cachedActivates
以name->Active
的形式進(jìn)行緩存; - 將
name
以","
的形式進(jìn)行分割成數(shù)組,然后遍歷;用緩存cachedNames
以class->name
的形式存儲(chǔ)當(dāng)前拓展點(diǎn)實(shí)現(xiàn)類的多個(gè)名稱捣作,同時(shí)誉结,以name->class
的形式將拓展點(diǎn)實(shí)現(xiàn)類緩存在cachedClasses
- 如果當(dāng)前類是
到這里,已經(jīng)完成了相關(guān)拓展點(diǎn)實(shí)現(xiàn)類券躁、@Adaptive
修飾的拓展點(diǎn)實(shí)現(xiàn)類惩坑、@Active
修飾的拓展點(diǎn)實(shí)現(xiàn)類、包裝Wrapper
拓展點(diǎn)實(shí)現(xiàn)類的加載嘱朽,回到getAdaptiveExtensionClass
,當(dāng)前緩存cachedAdaptiveClass
如果不為空旭贬,則說(shuō)明有@Adaptive
修飾的拓展點(diǎn)實(shí)現(xiàn)類,直接返回搪泳,如果沒(méi)有稀轨,則開始創(chuàng)建動(dòng)態(tài)自適應(yīng)拓展點(diǎn)createAdaptiveExtensionClass
.
createAdaptiveExtensionClass
private Class<?> createAdaptiveExtensionClass() {
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
//動(dòng)態(tài)編譯Extension,名字為typeName+"$Adaptive",如Protocol$Adaptive
//Protocol$Adaptive的主要功能
//1. 從url或擴(kuò)展接口獲取擴(kuò)展接口實(shí)現(xiàn)類的名稱岸军;
//2.根據(jù)名稱奋刽,獲取實(shí)現(xiàn)類ExtensionLoader.getExtensionLoader(擴(kuò)展接口類).getExtension(擴(kuò)展接口實(shí)現(xiàn)類名稱),然后調(diào)用實(shí)現(xiàn)類的方法艰赞。
//需要明白一點(diǎn)dubbo的內(nèi)部傳參基本上都是基于Url來(lái)實(shí)現(xiàn)的佣谐,也就是說(shuō)Dubbo是基于URL驅(qū)動(dòng)的技術(shù)
//所以,適配器類的目的是在運(yùn)行期獲取擴(kuò)展的真正實(shí)現(xiàn)來(lái)調(diào)用方妖,解耦接口和實(shí)現(xiàn)狭魂,這樣的話要不我們自己實(shí)現(xiàn)適配器類,要不dubbo幫我們生成,而這些都是通過(guò)Adpative來(lái)實(shí)現(xiàn)雌澄。
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
上面這個(gè)方法斋泄,通過(guò)動(dòng)態(tài)代理直接生成class,名為typeName
+$Adaptive
镐牺,debug得到Protocol$Adaptive,源碼內(nèi)容為
package com.wl.dubbo;
/**
* @Author: liumenglong
* @Date: 2018/9/29 22:48
* @Description:
*/
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.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;
//根據(jù)URL獲取到對(duì)應(yīng)的拓展名炫掐,如果沒(méi)有指定,則默認(rèn)取“dubbo”
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])");
//根據(jù)拓展名獲取對(duì)應(yīng)的拓展點(diǎn)實(shí)現(xiàn)類
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
//調(diào)用實(shí)際拓展點(diǎn)的refer方法睬涧,如DubboProtocol
return extension.refer(arg0, arg1);
}
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();
//根據(jù)URL獲取到對(duì)應(yīng)的拓展名募胃,如果沒(méi)有指定,則默認(rèn)取“dubbo”
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])");
//根據(jù)拓展名獲取對(duì)應(yīng)的拓展點(diǎn)實(shí)現(xiàn)類
com.alibaba.dubbo.rpc.Protocol extension =
(com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.
getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
//調(diào)用實(shí)際拓展點(diǎn)的export方法畦浓,如DubboProtocol
return extension.export(arg0);
}
}
何為動(dòng)態(tài)自適應(yīng)拓展點(diǎn)痹束,就是通過(guò)傳入的Url對(duì)象,獲取到相應(yīng)的拓展點(diǎn)名稱宅粥,根據(jù)拓展名獲取到具體的拓展點(diǎn)實(shí)現(xiàn)類参袱,從而調(diào)用該實(shí)現(xiàn)類的方法,這個(gè)方法很好的體現(xiàn)了對(duì)修改關(guān)閉對(duì)拓展開放
的原則秽梅,用戶如果需要實(shí)現(xiàn)新的拓展點(diǎn)實(shí)現(xiàn)類,只需要按規(guī)則配置好相應(yīng)的類和文件即可剿牺,無(wú)需修改核心代碼企垦。
再回到createAdaptiveExtension
,
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
上面的getAdaptiveExtensionClass
已經(jīng)完成相應(yīng)class
的加載,且通過(guò)newInstance
完成了實(shí)例化晒来,接下來(lái)進(jìn)行注入injectExtension
injectExtension
private T injectExtension(T instance) {
try {
//如果當(dāng)前objectFactory不為空钞诡,說(shuō)明傳入type不是ExtensionFactory類型(具體原因見當(dāng)前類的構(gòu)造方法),則可以注入
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
//判斷方法是否以set為前綴湃崩,且方法參數(shù)個(gè)數(shù)是1個(gè)荧降,且方法是public的,如果是攒读,進(jìn)入注入邏輯
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
//獲取該方法的class類型
Class<?> pt = method.getParameterTypes()[0];
try {
//獲取屬性名稱
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
// 根據(jù)類型和屬性名稱朵诫,獲取實(shí)例
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
//進(jìn)行注入
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;
}
- 判斷
objectFactory
,如果objectFactory
不為空薄扁,說(shuō)明說(shuō)明傳入type
不是ExtensionFactory
類型剪返,滿足注入的第一個(gè)條件 - 遍歷當(dāng)前對(duì)象的方法,如果方法滿足以下條件邓梅,進(jìn)行注入
- 前綴為"set"
- 是
public
修飾的 - 參數(shù)個(gè)數(shù)是一個(gè)
- 獲取屬性的類型
class
以及注入的屬性的名稱脱盲,通過(guò)objectFactory
進(jìn)行實(shí)例的獲取 - 調(diào)用
method.invoke(instance, object);
進(jìn)行注入
這里首先分析objectFactory
,因此需回到ExtensionLoader.getExtensionLoader
,本來(lái)應(yīng)該先分析這個(gè)方法,但由于getExtensionLoader
不影響前面加載拓展點(diǎn)實(shí)現(xiàn)類的邏輯日缨,故先不講解钱反,且前面加載拓展點(diǎn)實(shí)現(xiàn)類的邏輯是理解ExtensionLoader.getExtensionLoader
的基礎(chǔ),因此,直到注入這里面哥,再對(duì)ExtensionLoader.getExtensionLoader
進(jìn)行分析.
getExtensionLoader
//工廠方法哎壳,根據(jù)傳入類型獲取對(duì)應(yīng)ExtensionLoader
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//傳入的類型必須是接口且不為空,且必須是SPI修飾的可拓展點(diǎn)
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
//ExtensionLoader會(huì)存放在緩存之中幢竹,EXTENSION_LOADERS:type=>ExtensionLoader
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;
}
這里主要是對(duì)非SPI
修飾的類進(jìn)行攔截耳峦,然后根據(jù)傳入類型type
從EXTENSION_LOADERS
緩存中獲取實(shí)例,如果為空焕毫,則進(jìn)行實(shí)例創(chuàng)建蹲坷,然后添加到緩存中。
對(duì)實(shí)例創(chuàng)建的過(guò)程new ExtensionLoader<T>(type)
進(jìn)行解析:
進(jìn)入該構(gòu)造函數(shù):
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
由于當(dāng)前type
非ExtensionFactory
,故調(diào)用ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
,由上面可得邑飒,當(dāng)調(diào)用ExtensionLoader.getAdaptiveExtension
的時(shí)候,如果該拓展點(diǎn)的實(shí)現(xiàn)類有被@Adaptive
修飾的,則返回該實(shí)例循签,而查看ExtensionFactory
接口的實(shí)現(xiàn)類可得,有符合該條件的實(shí)現(xiàn)類,如圖
因此疙咸,objectFactory
不為空县匠,且是AdaptiveExtensionFactory
.
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
//獲取該拓展點(diǎn)的所有實(shí)現(xiàn)類的名稱,并進(jìn)行遍歷
for (String name : loader.getSupportedExtensions()) {
//往list里面添加根據(jù)name獲取到的拓展點(diǎn)實(shí)例
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
//遍歷所有的ExtensionFactory撒轮,當(dāng)獲取到的extension不為空的時(shí)候乞旦,則返回結(jié)果;
//如果是SPI拓展點(diǎn)题山,這里最后調(diào)用SpiExtensionFactory.getExtension兰粉,然后調(diào)用ExtensionLoader.getAdaptiveExtension方法返回拓展點(diǎn)實(shí)例
//如果非SPI拓展點(diǎn),這里通過(guò)SpringExtensionFactory獲取
// SpringExtensionFactory不支持SPI拓展點(diǎn)實(shí)例的獲取顶瞳,詳見SpringExtensionFactory的getExtension方法
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
首先看AdaptiveExtensionFactory
的構(gòu)造方法玖姑,獲取ExtensionFactory
的所有拓展點(diǎn)實(shí)現(xiàn)類,然后使用list
列表factories
存儲(chǔ)慨菱,這里list的內(nèi)容為SpiExtensionFactory
焰络、SpringExtensionFactory
,如圖
再看AdaptiveExtensionFactory.getExtension(Class<T> type, String name)
:
- 遍歷
factories
- 通過(guò)獲取到的
ObjectFactory.getExtension
符喝,獲取到對(duì)應(yīng)拓展點(diǎn)實(shí)例-
如果是傳入的類型是
SPI
拓展點(diǎn)闪彼,那么通過(guò)SpiExtensionFactory獲取,返回值被@Adaptive
修飾的拓展點(diǎn)或者動(dòng)態(tài)自適應(yīng)拓展點(diǎn)
-
如果傳入類型非
SPI
拓展點(diǎn)洲劣,通過(guò)SpringExtensionFactory獲取备蚓,從IOC容器中獲取
-
到這里,注入的邏輯也分析結(jié)束囱稽,總結(jié)下注入的邏輯:
- 如果當(dāng)前
type
非ExtensionFactory
類型郊尝,則可以進(jìn)行注入 -
ExtensionLoader
的objectFactory
為AdaptiveExtensionFactory
- 如果注入的類型是
SPI
修飾的接口,那么獲取到的值為@Adaptive
修飾的拓展點(diǎn)或者動(dòng)態(tài)自適應(yīng)拓展點(diǎn) - 如果注入的類型非
SPI
修飾的接口战惊,那么獲取的值是通過(guò)name
或者注入類型type
從IOC容器中獲取
總結(jié)
走讀了以上代碼流昏,對(duì)此過(guò)程做個(gè)總結(jié):
- 實(shí)例化
ExtensionLoader
,對(duì)當(dāng)前ExtensionLoader
的objectFactory
設(shè)值為AdaptiveExtensionFactory
,以注冊(cè)式單例存儲(chǔ)在緩存EXTENSION_LOADERS1
,類型為ConcurrentMap<Class<?>, ExtensionLoader<?>>
- 加載當(dāng)前拓展點(diǎn)實(shí)現(xiàn)類况凉,讀取指定文件下的指定文件谚鄙,分為三種類型的
class
的加載:-
@Adaptive
修飾的實(shí)現(xiàn)類的加載,存儲(chǔ)在緩存cachedAdaptiveClass
- 包裝類刁绒,存儲(chǔ)在
Set
緩存cachedWrapperClasses
- 普通的實(shí)現(xiàn)類(包含
@Active
修飾的闷营,且用緩存cachedActivates
存儲(chǔ),形式為name->Active
)存儲(chǔ)在cachedClasses
知市,存儲(chǔ)形式為name->class
,且為注冊(cè)式單例存儲(chǔ)
-
- 如果當(dāng)前拓展點(diǎn)有
@Adaptive
修飾的實(shí)現(xiàn)類,則返回該實(shí)現(xiàn)類的實(shí)例傻盟,且存儲(chǔ)在緩存cachedAdaptiveClass
,該值為單例,不可修改 - 如果當(dāng)前拓展點(diǎn)沒(méi)有
@Adaptive
修飾的實(shí)現(xiàn)類,返回動(dòng)態(tài)自適應(yīng)拓展點(diǎn)實(shí)現(xiàn)類嫂丙,例如Protocol
的動(dòng)態(tài)自適應(yīng)拓展點(diǎn)實(shí)現(xiàn)類為Protocol$Adaptive
,該拓展點(diǎn)的方法調(diào)用其實(shí)是根據(jù)傳入Url
的protocol
的類型來(lái)獲取具體的拓展點(diǎn)實(shí)現(xiàn)類娘赴,如配置為dubbo
協(xié)議的拓展點(diǎn)實(shí)現(xiàn)類為DubboProtocol