參考
屬于在這篇Dubbo擴(kuò)展點(diǎn)加載基礎(chǔ)上的展開(kāi)學(xué)習(xí)耍攘。但原文有點(diǎn)小問(wèn)題(Container啟動(dòng)那里)霜大,所以本文直接按自己的理解來(lái)組織癞谒。不加說(shuō)明的引用都來(lái)自該文啊掏。
問(wèn)題
Dubbo的原理梗搅、核心的概念很多文章都有詳細(xì)的介紹(什么SPI禾唁、擴(kuò)展點(diǎn)效览、Adaptive、ExtensionLoader等等)荡短,但是我的問(wèn)題是它們是如何運(yùn)行丐枉、如何起作用、整體的流程是什么掘托?
1. Dubbo入口
執(zhí)行Dubbo瘦锹,實(shí)際上是執(zhí)行了com.alibaba.dubbo.container.Main.main
Main這個(gè)類(lèi)中有一些靜態(tài)成員變量,最重要的是這個(gè):
private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
從這里開(kāi)始使用ExtensionLoader闪盔。
2. 初始化Main的靜態(tài)成員弯院,獲得Container類(lèi)型的ExtensionLoader
ExtensionLoader
類(lèi)中有個(gè)靜態(tài)的final變量EXTENSION_LOADERS
緩存了各種類(lèi)型的ExtensionLoader
,如果已有緩存泪掀,就直接獲取听绳,如果沒(méi)有就調(diào)用new方法新建。
啟動(dòng)時(shí)getExtensionLoader(Container.class)
獲取Container
類(lèi)型的ExtensionLoader
肯定不存在异赫,去構(gòu)造:
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
在ExtensionLoader
的構(gòu)造方法中可以看到椅挣,ExtensionLoader
有兩個(gè)屬性,一個(gè)是type
塔拳,標(biāo)識(shí)了這個(gè)ExtensionLoader
所屬的類(lèi)型鼠证,另一個(gè)是ObjectFactory
,類(lèi)型是ExtensionFactory
靠抑。
那么量九,ExtensionLoader<Container>
的this.type=Container.class
,
而根據(jù)構(gòu)造函數(shù)颂碧,欲要得到Container.Class
的ObjectFactory
娩鹉,須先得到ExtensionFactory
類(lèi)型的ExtensionLoader
。
2.1 獲取ExtensionFactory類(lèi)型的ExtensionLoader
同樣沒(méi)有稚伍,去構(gòu)造弯予,根據(jù)構(gòu)造函數(shù)ExtensionLoader<ExtensionFactory>
的this.type=ExtensionFactory.Class
,ObjectFactory=null
个曙。
繼續(xù)锈嫩。
2.2 獲取ExtensionFactory的擴(kuò)展實(shí)現(xiàn):getAdaptiveExtension()
Dubbo的微內(nèi)核做得非常的徹底,ExtensionFactory也是一個(gè)擴(kuò)展點(diǎn)垦搬,也需要通過(guò)ExtensionLoader<ExtensionFactory>加載
因?yàn)椋?code>Container的)ObjectFactory
是ExtensionFactory
類(lèi)型呼寸,而ExtensionFactory
是一個(gè)擴(kuò)展點(diǎn),那么就要用ExtensionFactory.Class
的ExtensionLoader
通過(guò)getAdaptiveExtension()
獲取到ExtensionFactory.Class
的Adaptive實(shí)現(xiàn)猴贰,再給到(Container
的)ObjectFactory
对雪。
在getAdaptiveExtension()
中,首先也是試圖從ExtensionLoader<ExtensionFactory>
的一個(gè)緩存cachedAdaptiveInstance
中取米绕,取不到就調(diào)用createAdaptiveExtension()
創(chuàng)建瑟捣。
2.2.1 創(chuàng)建ExtensionFactory擴(kuò)展: createAdaptiveExtension()
邏輯就一句:
injectExtension((T) getAdaptiveExtensionClass().newInstance());
先看 getAdaptiveExtensionClass()
馋艺。
2.2.1.1 獲取ExtensionFactory擴(kuò)展類(lèi):getAdaptiveExtensionClass()
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
這里整體的邏輯是,先getExtensionClasses()
迈套,看看有沒(méi)有已經(jīng)存在的擴(kuò)展實(shí)現(xiàn)類(lèi)捐祠,有的話(huà)取到這些類(lèi),把標(biāo)記了@Adaptive
的緩存到cachedAdaptiveClass
(比如說(shuō)ExtensionFactory
就是有的)桑李,把沒(méi)有標(biāo)記@Adaptive
的擴(kuò)展實(shí)現(xiàn)類(lèi)緩存到cachedClasses
踱蛀,返回。
如果沒(méi)有的話(huà)贵白,就調(diào)用createAdaptiveExtensionClass()
現(xiàn)生成code并compile一個(gè)Adaptive類(lèi)出來(lái)率拒,同樣緩存到cachedAdaptiveClass
,返回禁荒。
Adaptive注解可以標(biāo)記在類(lèi)上猬膨,也可以標(biāo)記在方法上。
如果Adaptive類(lèi)選擇擴(kuò)展點(diǎn)實(shí)現(xiàn)的依據(jù)是根據(jù)上下文信息圈浇,即URL中的信息選擇不同的實(shí)現(xiàn),那么可以把Adaptive注解標(biāo)記在服務(wù)的方法上靴寂,ExtensionLoader可以自動(dòng)生成這種情況的Adaptive類(lèi)磷蜀。
如果是自己實(shí)現(xiàn)Adaptive類(lèi),那么需要在類(lèi)上標(biāo)記Adaptive注解百炬,ExtensionLoader在加載擴(kuò)展點(diǎn)時(shí)就能發(fā)現(xiàn)并創(chuàng)建Adaptive實(shí)現(xiàn)褐隆。
(來(lái)自Dubbo源碼解析-2:Adaptive類(lèi),同樣很厲害的文章嗯剖踊,本文對(duì)其也多有引用不一一列舉了)
下面詳細(xì)分析一下這兩步庶弃。
2.2.1.1.1 獲取已存在的擴(kuò)展類(lèi):getExtensionClasses()
首先檢查ExtensionLoader<ExtensionFactory>
的緩存cachedClasses
,有就get沒(méi)有就調(diào)用loadExtensionClasses()
創(chuàng)建德澈。
- loadExtensionClasses()
首先去拿SPI注解歇攻,如果注解不為null,獲取注解的value:
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
...
String value = defaultAnnotation.value();
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);
}
SPI注解類(lèi)定義如下:
public @interface SPI {
/**
* 缺省擴(kuò)展點(diǎn)名梆造。
*/
String value() default "";
}
可以看到ExtensionFactory沒(méi)有定義缺省擴(kuò)展點(diǎn)名(后面會(huì)看到Container類(lèi)有缺省定義)缴守。
有缺省擴(kuò)展點(diǎn)名就緩存到ExtensionLoader<ExtensionFactory>
的另一個(gè)緩存cachedDefaultName
,沒(méi)有就繼續(xù)镇辉。
繼續(xù)屡穗,調(diào)用loadFile
裝載擴(kuò)展實(shí)現(xiàn)類(lèi)。
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
- loadFile(Map<String, Class<?>> extensionClasses, String dir)
loadFile首先在指定目錄下找名字是type的配置文件忽肛,然后讀出來(lái):
String fileName = dir + type.getName();
對(duì) 于 ExtensionFactory
來(lái) 說(shuō) 村砂, 會(huì)讀到:
//META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
//META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
隨后:
loadfile讀入第一行數(shù)據(jù),初始化:
name=adaptive, line = com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
屹逛。使用
Class<?> clazz = Class.forName(line, true, classLoader)
加載類(lèi)础废。!type.isAssignableFrom(clazz)
:驗(yàn)證加載的類(lèi)是否是當(dāng)前type的一個(gè)實(shí)現(xiàn)汛骂。clazz.isAnnotationPresent(Adaptive.class)
:檢查如果設(shè)置了@Adaptive
,則把clazz
保存在cachedAdaptiveClass
中
對(duì)于ExtensionFactory
來(lái)說(shuō)色迂,AdaptiveExtensionFactory
設(shè)置了@Adaptive
注解:
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
...
}
- 對(duì)于沒(méi)有設(shè)置
@Adaptive
的類(lèi)香缺,則存入loadExtensionClasses()
傳到loadFile()
中的參數(shù)extensionClasses
,返回后在getExtensionClasses()
中賦給cachedClasses
緩存歇僧。
classes = loadExtensionClasses();
cachedClasses.set(classes);
對(duì)于ExtensionFactory
來(lái)說(shuō)图张,SpiExtensionFactory
和SpringExtensionFactory
都沒(méi)有設(shè)置@Adaptive
注解(同一個(gè)類(lèi)只能有一個(gè)Adaptive
實(shí)現(xiàn)),所以都被存入了ExtensionLoader<ExtensionFactory>
的cachedClasses
诈悍。
2.2.1.1.2 動(dòng)態(tài)生成沒(méi)有自己實(shí)現(xiàn)的Adaptive類(lèi):createAdaptiveExtensionClass()
執(zhí)行完getExtensionClasses()
祸轮,回到了2.2.1.1的getAdaptiveExtensionClass()
,如果此時(shí)cachedAdaptiveClass
仍為null
侥钳,說(shuō)明沒(méi)有找到標(biāo)記了@Adaptive
的類(lèi)适袜,需要根據(jù)上下文動(dòng)態(tài)生成。
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
動(dòng)態(tài)生成的過(guò)程簡(jiǎn)單來(lái)說(shuō)就是找到標(biāo)記了@Adaptive
的方法舷夺,根據(jù)type通過(guò)StringBuilder
拼接一個(gè)類(lèi)苦酱,加載Compiler
編譯。
前面提到的參考文章(Dubbo源碼解析-2:Adaptive類(lèi))里舉了例子给猾,可以看到生成了什么樣子的代碼疫萤。這里不詳細(xì)說(shuō)了。
2.2.1.2 為擴(kuò)展注入依賴(lài)的其他擴(kuò)展實(shí)現(xiàn):injectExtension(T instance)
好的終于回來(lái)了敢伸,2.2.1.1節(jié)完成了getAdaptiveExtensionClass()
扯饶,并返回了cachedAdaptiveClass
。 為這個(gè)cachedAdaptiveClass
new了一個(gè)Instance以后池颈,開(kāi)始injectExtension
注入尾序。
擴(kuò)展點(diǎn)注入的代碼如下,首先查找以set開(kāi)頭躯砰、帶一個(gè)參數(shù)的public方法每币,如果set方法的名稱(chēng)大于3,則根據(jù)名稱(chēng)獲取參數(shù)擴(kuò)展點(diǎn)的名稱(chēng)琢歇,然后獲取擴(kuò)展點(diǎn)實(shí)現(xiàn)的實(shí)例脯爪,這可能又是一個(gè)次配置的解析和實(shí)例化過(guò)程。最后調(diào)用set方法將實(shí)例設(shè)置進(jìn)去矿微。
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")&&...) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
...
}
}
}
}
} catch (Exception e) {
...
}
return instance;
}
對(duì)于ExtensionFactory
來(lái)說(shuō)痕慢,首先判斷,if (objectFactory != null)
涌矢,則可以直接返回cachedAdaptiveClass
所new的instance
了掖举。
2.2.2 ExtensionLoader<Container>構(gòu)造完畢
整個(gè)2.2.1過(guò)程結(jié)束,拿到了ExtensionFactory.class
的擴(kuò)展實(shí)現(xiàn)娜庇,也就是所返回的cachedAdaptiveClass
塔次,即AdaptiveExtensionFactory
方篮。
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
回到構(gòu)造函數(shù),ExtensionLoader<Container>
的objectFactory =AdaptiveExtensionFactory
励负。
ExtensionLoader<Container>
構(gòu)造完畢藕溅。
3. 在main()中調(diào)用loader.getExtension()加載Container的擴(kuò)展實(shí)現(xiàn)
Main函數(shù)的整體邏輯是,如果沒(méi)有傳入?yún)?shù)继榆,就裝載Container的SPI注解指定的默認(rèn)Container巾表,如果傳入的參數(shù)(main函數(shù)參數(shù)、JVM啟動(dòng)參數(shù)略吨、classpath下的dubbo.properties配置等)指定了Container集币,就裝載通過(guò)參數(shù)指定的Container。
public class Main {
public static final String CONTAINER_KEY = "dubbo.container";
private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
...
public static void main(String[] args) {
try {
if (args == null || args.length == 0) {
String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
args = Constants.COMMA_SPLIT_PATTERN.split(config);
}
final List<Container> containers = new ArrayList<Container>();
for (int i = 0; i < args.length; i ++) {
containers.add(loader.getExtension(args[i]));
}
...
for (Container container : containers) {
container.start();
...
}
...
}
}
首先看Container類(lèi)的定義:
@SPI("spring")
public interface Container {
/**
* start.
*/
void start();
/**
* stop.
*/
void stop();
}
它將SPI的value值設(shè)置Spring
翠忠,也就是指定默認(rèn)的擴(kuò)展實(shí)現(xiàn)名稱(chēng)是spring(默認(rèn)情況只加載一個(gè)spring容器)鞠苟。
再看關(guān)于Container的配置(以spring為例,其他jetty秽之、log4j等都類(lèi)似):
\\META-INF/dubbo/internal/com.alibaba.dubbo.container.Container
spring=com.alibaba.dubbo.container.spring.SpringContainer
而SpringContainer類(lèi)沒(méi)有加注解什么的当娱,所以這里是通過(guò)SPI注解的默認(rèn)值控制的,和ExtensionFactory的機(jī)制不同考榨。
下面詳細(xì)分析main函數(shù)加載Container擴(kuò)展點(diǎn)過(guò)程跨细。
3.1 如果沒(méi)有傳參,獲得默認(rèn)擴(kuò)展名:loader.getDefaultExtensionName()
public String getDefaultExtensionName() {
getExtensionClasses();
return cachedDefaultName;
}
這里調(diào)用了getExtensionClasses()
董虱,與2.2.1.1.1節(jié)介紹的過(guò)程相同扼鞋。
不同的是申鱼,對(duì)于ExtensionLoader<Container>
愤诱,通過(guò)SPI注解定義了缺省擴(kuò)展點(diǎn)名spring
,因此會(huì)將spring
緩存到cachedDefaultName
捐友。
而由于Container的擴(kuò)展實(shí)現(xiàn)類(lèi)都沒(méi)有設(shè)置@Adaptive
淫半,則這些實(shí)現(xiàn)類(lèi)都被緩存在cachedClasses
中,不會(huì)被緩存在cachedAdaptiveClass
匣砖。
3.2 獲得Container:loader.getExtension()
- getExtension()
先去緩存cachedInstances
中找科吭,如果沒(méi)有,則調(diào)用createExtension
創(chuàng)建猴鲫。
public T getExtension(String name) {
...
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) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
- createExtension()
通過(guò)getExtensionClasses()
獲得擴(kuò)展實(shí)現(xiàn)類(lèi)对人,調(diào)用injectExtension
注入依賴(lài)的其他擴(kuò)展和包裝類(lèi)。返回獲取了包裝后的擴(kuò)展instance拂共。
private T createExtension(String name) {
Class<?> clazz = getExtensionClasses().get(name);
...
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
...
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) {
...
}
}
- getExtensionClasses()
其實(shí)是從獲取之前l(fā)oad到cachedClassed中的所有擴(kuò)展實(shí)現(xiàn)類(lèi)牺弄。沒(méi)有的話(huà)就調(diào)用loadExtensionClasses()
(見(jiàn)2.2.1.1.1節(jié))獲取。
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
...
classes = loadExtensionClasses();
...
}
return classes;
}
3.3 獲取Container擴(kuò)展后宜狐,啟動(dòng)
擴(kuò)展加載結(jié)束势告,啟動(dòng)Container~
for (Container container : containers) {
container.start();
...
}