本篇其實(shí)更應(yīng)該在Dubbo系列的最早去寫禁炒,只不過因?yàn)楫?dāng)初在讀源碼的時(shí)候沒有開始更新博客窜锯,所以漏了重要的一個章節(jié)饿凛,那就是Dubbo的SPI機(jī)制,所以這一章節(jié)主要是講解一下Dubbo的SPI機(jī)制實(shí)現(xiàn)移层。
我們都是知道一個合格的開源框架對于擴(kuò)展的支持都要是相當(dāng)彈性的,Dubbo 也不例外轻腺。Dubbo采用了簡單的擴(kuò)展方式劫哼,基于SPI機(jī)制。
什么是SPI样眠?
java spi的具體約定為:當(dāng)服務(wù)的提供者友瘤,提供了服務(wù)接口的一種實(shí)現(xiàn)之后,在jar包的META-INF/services/目錄里同時(shí)創(chuàng)建一個以服務(wù)接口命名的文件檐束。該文件里就是實(shí)現(xiàn)該服務(wù)接口的具體實(shí)現(xiàn)類辫秧。而當(dāng)外部程序裝配這個模塊的時(shí)候,就能通過該jar包META-INF/services/里的配置文件找到具體的實(shí)現(xiàn)類名被丧,并裝載實(shí)例化盟戏,完成模塊的注入。 基于這樣一個約定就能很好的找到服務(wù)接口的實(shí)現(xiàn)類甥桂,而不需要再代碼里制定柿究。jdk提供服務(wù)實(shí)現(xiàn)查找的一個工具類:java.util.ServiceLoader。 (網(wǎng)上摘錄的解釋)
Dubbo的SPI主要改進(jìn)了JDK標(biāo)準(zhǔn)的SPI實(shí)現(xiàn):
JDK標(biāo)準(zhǔn)的SPI會一次性實(shí)例化擴(kuò)展點(diǎn)所有實(shí)現(xiàn)黄选,如果有擴(kuò)展實(shí)現(xiàn)初始化很耗時(shí)蝇摸,但如果沒用上也加載,會很浪費(fèi)資源。
如果擴(kuò)展點(diǎn)加載失敗探入,連擴(kuò)展點(diǎn)的名稱都拿不到了狡孔。比如:JDK標(biāo)準(zhǔn)的ScriptEngine,通過getName();獲取腳本類型的名稱蜂嗽,但如果RubyScriptEngine因?yàn)樗蕾嚨膉ruby.jar不存在苗膝,導(dǎo)致RubyScriptEngine類加載失敗,這個失敗原因被吃掉了植旧,和ruby對應(yīng)不起來辱揭,當(dāng)用戶執(zhí)行ruby腳本時(shí),會報(bào)不支持ruby病附,而不是真正失敗的原因问窃。
增加了對擴(kuò)展點(diǎn)IoC和AOP的支持,一個擴(kuò)展點(diǎn)可以直接setter注入其它擴(kuò)展點(diǎn)完沪。
基本上講到這里大家對于SPI可能有個大致的認(rèn)識域庇,但是要真正理解Dubbo的SPI,還是要仔細(xì)看一下源碼才可以覆积。
在理解Dubbo的SPI之前听皿,要明確幾個核心概念:
擴(kuò)展點(diǎn) Dubbo作用靈活的框架,并不會強(qiáng)制所有用戶都一定使用Dubbo提供的某些架構(gòu)宽档。例如注冊中心(Registry)尉姨,Dubbo提供了zk和redis,但是如果我們更傾向于其他的注冊中心的話吗冤,我們可以替換掉Dubbo提供的注冊中心又厉。針對這種可被替換的技術(shù)實(shí)現(xiàn)點(diǎn)我們稱之為擴(kuò)展點(diǎn),類似的擴(kuò)展點(diǎn)有很多椎瘟,例如Protocol覆致,F(xiàn)ilter,Loadbalance等等降传。
Wrapper Dubbo在加載某個接口的擴(kuò)展類時(shí)候篷朵,如果某個實(shí)現(xiàn)中有一個拷貝類構(gòu)造函數(shù)勾怒,那么該接口實(shí)現(xiàn)就是該接口的包裝類婆排,此時(shí)Dubbo會在真正的實(shí)現(xiàn)類上層包裝上蓋Wrapper。即這個時(shí)候從ExtensionLoader中返回的實(shí)際擴(kuò)展類是被Wrapper包裝的接口實(shí)現(xiàn)類笔链。
Adaptive 這個自適應(yīng)的擴(kuò)展點(diǎn)比較難理解段只,所以這里直接以一個例子來講解:在RegistryProtocol中有一個屬性為Cluster,其中Protocol和Cluster都是Dubbo提供的擴(kuò)展點(diǎn)鉴扫,所以這時(shí)候當(dāng)我們真正在操作中使用cluster的時(shí)候究竟使用的哪一個cluster的實(shí)現(xiàn)類呢赞枕?是FailbackCluster還是FailoverCluster?Dubbo在加載一個擴(kuò)展點(diǎn)的時(shí)候如果發(fā)現(xiàn)其成員變量也是一個擴(kuò)展點(diǎn)并且有相關(guān)的set方法,就會在這時(shí)候?qū)⒃摂U(kuò)展點(diǎn)設(shè)置為一個自適應(yīng)的擴(kuò)展點(diǎn)炕婶,自適應(yīng)擴(kuò)展點(diǎn)(Adaptive)會在真正使用的時(shí)候從URL中獲取相關(guān)參數(shù)姐赡,來調(diào)用真正的擴(kuò)展點(diǎn)實(shí)現(xiàn)類。具體的實(shí)現(xiàn)會在下面的源碼中詳細(xì)解釋柠掂。對于Adaptive的理解其實(shí)個人推薦的是Dubbo開發(fā)者指南项滑,指南中有對于Adaptive的明確介紹。
Activate 官網(wǎng)的叫法是自激活涯贞,其實(shí)這個更合適的叫法我認(rèn)為是條件激活枪狂,我們還記得上一篇中有提到Filter的內(nèi)容,其中Filter鏈的獲取就是通過@Activate注解來確定的宋渔,所以Activate的作用主要是:提供一種選擇性激活的條件州疾,可以是我們通過相關(guān)的配置來確定激活哪些功能。
ExtensionLoader
之所以把ExtensionLoader標(biāo)這么大是因?yàn)槠涮匾始穑鳛檎麄€SPI的核心严蓖,ExtensionLoader起著無可替代的作用,下面的整篇文章都在圍繞著這個類進(jìn)行講解氧急,足以看出他是多么重要了谈飒。
鑒于ExtensionLoade的用法比較多的都是如下用法,我就以下面的調(diào)用為例開始介紹ExtensionLoader (調(diào)用的鏈路比較長态蒂,大家要耐心點(diǎn)哈)
//在Dubbo源碼中大面積使用這種寫法杭措,都是獲得某個接口的適配類,在真正執(zhí)行的時(shí)候才決定最終的作用類
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
//因?yàn)槊恳粋€擴(kuò)展類加載器只能加載特定的SPI擴(kuò)展的實(shí)現(xiàn)钾恢,所以要獲得某個擴(kuò)展的實(shí)現(xiàn)的話首先要找到他對應(yīng)的擴(kuò)展類加載器(ExtensionLoader)
//一個擴(kuò)展接口的所有實(shí)現(xiàn)都是被同一個擴(kuò)展類加載器來加載的
@SuppressWarnings("unchecked")
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!");
}
//獲得某個擴(kuò)展類加載器的時(shí)候該接口必須被@SPI修飾才可以
if(!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
// 先從靜態(tài)緩存中獲取對應(yīng)的ExtensionLoader實(shí)例手素,每個接口對應(yīng)一個ExtensionLoader并且把映射關(guān)系都存儲在緩存之中
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
// 為Extension類型創(chuàng)建ExtensionLoader實(shí)例,并放入靜態(tài)緩存
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if(createAdaptiveInstanceError == null) {
//采用單例模式的雙重判斷瘩蚪,這種模式感覺使用的范圍都比較廣泛
//cachedAdaptiveInstance作為一個Holder(只有簡單的get和set方法)泉懦,也是一個鎖對象
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
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)建一個接口的適配類
private T createAdaptiveExtension() {
try {
//獲取AdaptiveExtensionClass并完成注入
//基本分兩步:1.獲取適配器類 2.在適配器里面注入其他的擴(kuò)展點(diǎn)
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
}
}
//獲得適配類有兩種途徑,第一就是某個實(shí)現(xiàn)類上被@Adaptive注解疹瘦,第二就是沒有實(shí)現(xiàn)類被注解崩哩,因此Dubbo會自動生成一個某個接口的適配類
private Class<?> getAdaptiveExtensionClass() {
//如果能找到被@Adaptive注解實(shí)現(xiàn)類
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//找不到的話就自行創(chuàng)建一個適配類
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
//加載當(dāng)前擴(kuò)展所有實(shí)現(xiàn),看是否有實(shí)現(xiàn)類上被標(biāo)注為@Adaptive
private Map<String, Class<?>> getExtensionClasses() {
//多次判斷是為了防止同一個擴(kuò)展點(diǎn)被多次加載
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//loadExtensionClasses會加載所有的配置文件言沐,將配置文件中對應(yīng)的的類加載到當(dāng)前的緩存中
//load完之后該classes已經(jīng)保留了所有的擴(kuò)展類映射關(guān)系
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
private Map<String, Class<?>> loadExtensionClasses() {
//所有的擴(kuò)展點(diǎn)接口都必須被SPI注釋標(biāo)注
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) {
String value = defaultAnnotation.value();
if(value != null && (value = value.trim()).length() > 0) {
//一個@SPI注解的值只能有一個
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));
}
//cachedDefaultName表示該擴(kuò)展點(diǎn)對應(yīng)的默認(rèn)適配類的key
//邏輯運(yùn)行到這里就意味著該擴(kuò)展點(diǎn)有定義的適配類邓嘹,不需要Dubbo框架自己生成適配類
if(names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
//加載對應(yīng)目錄下的配置文件,三個目錄分別為:META-INF/services/险胰,META-INF/dubbo汹押,META-INF/dubbo/internal
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
//加載相關(guān)路徑下的類文件
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
//加載這些問價(jià)你的classloader要和加載當(dāng)前類的classloader一致,這個類似與Java默認(rèn)的類加載器和類的加載關(guān)系
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
//該步驟就加載所有的classpath下面的同名文件(包含你的項(xiàng)目本地classpath和依賴jar包)
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
//一般情況下每個包內(nèi)只會對與每個擴(kuò)展點(diǎn)放置一個類信息描述文件
while (urls.hasMoreElements()) {
java.net.URL url = urls.nextElement();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
try {
String line = null;
while ((line = reader.readLine()) != null) {
//處理注釋內(nèi)容
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();//SPI擴(kuò)展文件中的key
line = line.substring(i + 1).trim();//SPI擴(kuò)展文件中配置的value ExtensionLoader是根據(jù)key和value同時(shí)加載的
}
if (line.length() > 0) {
//加載擴(kuò)展類
Class<?> clazz = Class.forName(line, true, classLoader);
//如果配置的擴(kuò)展類實(shí)現(xiàn)不是目標(biāo)接口的實(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.");
}
//如果配置的類是被@Adaptive注解的話
if (clazz.isAnnotationPresent(Adaptive.class)) {
if(cachedAdaptiveClass == null) {
//將緩存的AdaptiveClass設(shè)置成此類
cachedAdaptiveClass = clazz;
// 一個接口只能有一個適配類
} else if (! cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else {
try {
//判斷有沒有拷貝構(gòu)造函數(shù),如果有的話說明該類是實(shí)現(xiàn)的包裝類起便,進(jìn)行緩存棚贾。一個接口可能有多個對應(yīng)的包裝類實(shí)現(xiàn)
clazz.getConstructor(type);
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
clazz.getConstructor();
if (name == null || name.length() == 0) {
//兼容老邏輯窖维,這里可以暫時(shí)忽略
name = findAnnotationName(clazz);
if (name == null || name.length() == 0) {
if (clazz.getSimpleName().length() > type.getSimpleName().length()
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
} else {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
}
}
}
//將配置的key名字根據(jù)逗號來區(qū)分
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
//自激活的實(shí)現(xiàn)類只會將第一個name進(jìn)行存儲,原因到現(xiàn)在還有點(diǎn)不清楚
cachedActivates.put(names[0], activate);
}
for (String n : names) {
//假如說配置了name1,name2=com.alibaba.DemoInterface,這時(shí)候在cachedNames中只會存儲一個name1
if (! cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
//防止同一個擴(kuò)展類實(shí)現(xiàn)被兩個key共同使用
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
} // end of while read lines
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + url + ") in " + url, t);
}
} // end of while urls
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
//通過反射自動調(diào)用instance的set方法把自身的屬性注入進(jìn)去妙痹,解決的擴(kuò)展類依賴問題铸史,也就是說解決擴(kuò)展類依賴擴(kuò)展類的問題
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) { //如果該擴(kuò)展點(diǎn)實(shí)例有Set開頭的公共方法
Class<?> pt = method.getParameterTypes()[0];//得到set方法的參數(shù)類型
try {
//得到屬性名稱,比如setName方法就得到name屬性名稱
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);// 如果不為空怯伊,說明set方法的參數(shù)是擴(kuò)展點(diǎn)類型沛贪,那么進(jìn)行注入,意思也就是說擴(kuò)展點(diǎn)里面還有依賴其他擴(kuò)展點(diǎn)
}
} 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)用instance的set方法把自身的屬性注入進(jìn)去震贵,解決的擴(kuò)展類依賴問題利赋,也就是說解決擴(kuò)展類依賴擴(kuò)展類的問題
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) { //如果該擴(kuò)展點(diǎn)實(shí)例有Set開頭的公共方法
Class<?> pt = method.getParameterTypes()[0];//得到set方法的參數(shù)類型
try {
//得到屬性名稱,比如setName方法就得到name屬性名稱
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);// 如果不為空猩系,說明set方法的參數(shù)是擴(kuò)展點(diǎn)類型媚送,那么進(jìn)行注入,意思也就是說擴(kuò)展點(diǎn)里面還有依賴其他擴(kuò)展點(diǎn)
}
} 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;
}
基本上講到之類寇甸,一個適配類已經(jīng)可以被加載出來了塘偎,但是由于上面的邏輯比較深,下面給出簡單文字邏輯:
1.為了獲得一個擴(kuò)展點(diǎn)的適配類拿霉,首先會看緩存中有沒有已經(jīng)加載過的適配類吟秩,如果有的話就直接返回,沒有的話就進(jìn)入第2步绽淘。
2.加載所有的配置文件涵防,將所有的配置類都load進(jìn)內(nèi)存并且在ExtensionLoader內(nèi)部做好緩存,如果配置的文件中有適配類就緩存起來沪铭,如果沒有適配類就自行通過代碼自行創(chuàng)建適配類并且緩存起來(代碼之后給出樣例)壮池。
3.在加載配置文件的時(shí)候,會依次將包裝類杀怠,自激活的類都進(jìn)行緩存椰憋。
4.將獲取完適配類時(shí)候,如果適配類的set方法對應(yīng)的屬性也是擴(kuò)展點(diǎn)話赔退,會依次注入對應(yīng)的屬性的適配類(循環(huán)進(jìn)行)橙依。
講到這里的話適配類這段代碼邏輯已經(jīng)大致都解釋過了,下面看一下Dubbo自己生成的適配類代碼是怎樣的(以Protocol為例):
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements Protocol {
public Invoker refer(Class arg0, URL arg1) throws Class {
if (arg1 == null) throw new IllegalArgumentException("url == null");
URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(Protocol) name from url(" + url.toString() + ") use keys([protocol])");
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public Exporter export(Invoker arg0) throws Invoker {
if (arg0 == null) throw new IllegalArgumentException("Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("Invoker argument getUrl() == null");URL url = arg0.getUrl();
//這里會根據(jù)url中的信息獲取具體的實(shí)現(xiàn)類名
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(Protocol) name from url(" + url.toString() + ") use keys([protocol])");
//根據(jù)上面的實(shí)現(xiàn)類名硕旗,會在運(yùn)行時(shí)窗骑,通過Dubbo的擴(kuò)展機(jī)制加載具體實(shí)現(xiàn)類
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public void destroy() {
throw new UnsupportedOperationException("method public abstract void Protocol.destroy() of interface Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int Protocol.getDefaultPort() of interface Protocol is not adaptive method!");
}
}
本質(zhì)上的做法就是通過方法的參數(shù)獲得URL信息,從URL中獲得對應(yīng)的value對應(yīng)值卵渴,然后從ExtensionLoader的緩存中找到value對應(yīng)的具體實(shí)現(xiàn)類慧域,然后用該實(shí)現(xiàn)類進(jìn)行工作±硕粒可以看到上面的核心就是getExtension方法了昔榴,下面來看一下該方法的實(shí)現(xiàn):
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
//DefaultExtension就是自適應(yīng)的擴(kuò)展類
if ("true".equals(name)) {
return getDefaultExtension();
}
//先從緩存中去取
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
//如果緩存中沒有的話在創(chuàng)建一個然后放進(jìn)去,但是此時(shí)并沒有實(shí)際內(nèi)容碘橘,只有一個空的容器Holder
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) {
//根據(jù)名字創(chuàng)建特定的擴(kuò)展
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
private T createExtension(String name) {
//獲取name類型對應(yīng)的擴(kuò)展類型互订,從cachedClasses根據(jù)key獲取對應(yīng)的class,cachedClasses已經(jīng)在load操作的時(shí)候初始化過了痘拆。
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
//獲得擴(kuò)展類型對應(yīng)的實(shí)例
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
//將實(shí)例放進(jìn)緩存中
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//injectExtension方法的作用就是通過set方法注入其他的屬性擴(kuò)展點(diǎn)仰禽,上面已經(jīng)講過
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
//循環(huán)遍歷所有wrapper實(shí)現(xiàn),實(shí)例化wrapper并進(jìn)行擴(kuò)展點(diǎn)注入
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);
}
}
getExtension的步驟就是根據(jù)之前l(fā)oad操作生成的class緩存纺蛆,找到對應(yīng)的Class信息吐葵,然后根據(jù)Class生成具體的實(shí)例,生成之后通過反射注入其他的屬性擴(kuò)展點(diǎn)桥氏,將包裝類進(jìn)行層層包裝温峭,最終返回包裝過的類。(包裝邏輯可以參考Protocol的包裝實(shí)例)
理解ExtensionLoader是理解Dubbo的SPI的基礎(chǔ)字支,也是理解Dubbo擴(kuò)展機(jī)制的基礎(chǔ)凤藏,所以如果各位有耐心的話,還是請自己閱讀一些上面提到的源碼部分(基本上都有文字注釋)堕伪。
最后揖庄,習(xí)慣性的撒花~~~