摘要:要構(gòu)建自適應(yīng)實(shí)例,先要有自適應(yīng)的實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)類(lèi)有兩種方式一種通過(guò)配置文件,一種是通過(guò)是字節(jié)碼的方式動(dòng)態(tài)生成。
SPI機(jī)制
SPI,即(service provider interface)機(jī)制,有很多組件的實(shí)現(xiàn),如日志、數(shù)據(jù)庫(kù)訪問(wèn)等都是采用這樣的方式,一般通用組件為了提升可擴(kuò)展性,基于接口編程,將操作接口形成標(biāo)準(zhǔn)規(guī)范,但是可以開(kāi)放多種擴(kuò)展實(shí)現(xiàn),這種做法也符合開(kāi)閉設(shè)計(jì)原則,使組件具有可插撥特性。不同的廠商或組織可以基于規(guī)范推出自己的實(shí)現(xiàn),只需要在自己的jar包中通過(guò)配置文件和相應(yīng)的實(shí)現(xiàn)類(lèi)即可以實(shí)現(xiàn)擴(kuò)展。甚至開(kāi)發(fā)者自己也可以很方便對(duì)框架進(jìn)行定制化實(shí)現(xiàn)。
JDK SPI介紹JDK實(shí)現(xiàn)spi服務(wù)查找: ServiceLoader。
舉個(gè)例子:
首先定義下示例接口
package com.example; public interface Spi { booleanisSupport(String name); String sayHello(); }
ServiceLoader會(huì)遍歷所有jar查找META-INF/services/com.example.Spi文件
A廠商提供實(shí)現(xiàn)
package com.a.example; public class SpiAImpl implements Spi { publicboolean isSupport(String name) { return"SPIA".equalsIgnoreCase(name.trim()); } public String syaHello() { return “hello 我是廠商A”; } }
在A廠商提供的jar包中的META-INF/services/com.example.Spi文件內(nèi)容為:
com.a.example.SpiAImpl #廠商A的spi實(shí)現(xiàn)全路徑類(lèi)名
B廠商提供實(shí)現(xiàn)
package com.b.example; public class SpiBImpl implements Spi { publicboolean isSupport(String name) { return"SPIB".equalsIgnoreCase(name.trim()); } public String syaHello() { return “hello 我是廠商B”; } }
在B廠商提供的jar包中的META-INF/services/com.example.Spi文件內(nèi)容為:
com.b.example.SpiBImpl #廠商B的spi實(shí)現(xiàn)全路徑類(lèi)名
ServiceLoader.load(Spi.class)讀取廠商A、B提供jar包中的文件,ServiceLoader實(shí)現(xiàn)了Iterable接口可通過(guò)while for循環(huán)語(yǔ)句遍歷出所有實(shí)現(xiàn)。
一個(gè)接口多種實(shí)現(xiàn),就如策略模式一樣提供了策略的實(shí)現(xiàn),但是沒(méi)有提供策略的選擇, 使用方可以根據(jù)isSupport方法根據(jù)業(yè)務(wù)傳入廠商名來(lái)選擇具體的廠商。
public class SpiFactory { //讀取配置獲取所有實(shí)現(xiàn) privatestatic ServiceLoader spiLoader = ServiceLoader.load(Spi.class); //根據(jù)名字選取對(duì)應(yīng)實(shí)現(xiàn) publicstatic Spi getSpi(String name) { for(Spi spi : spiLoader) { if(spi.isSupport(name) ) { returnspi; } } returnnull; } }Duddo SPI
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)的名稱(chēng)都拿不到了。
增加了對(duì)擴(kuò)展點(diǎn) IoC 和 AOP 的支持,一個(gè)擴(kuò)展點(diǎn)可以直接 setter 注入其它擴(kuò)展點(diǎn)。
示例在擴(kuò)展類(lèi)的jar包內(nèi),放置擴(kuò)展點(diǎn)配置文件 META-INF/dubbo/接口全限定名,內(nèi)容為:配置名=擴(kuò)展實(shí)現(xiàn)類(lèi)全限定名,多個(gè)實(shí)現(xiàn)類(lèi)用換行符分隔。
以擴(kuò)展 Dubbo 的協(xié)議為例,在協(xié)議的實(shí)現(xiàn) jar 包內(nèi)放置文本文件:META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol,內(nèi)容為:
xxx=com.alibaba.xxx.XxxProtocol
實(shí)現(xiàn)類(lèi)內(nèi)容:
package com.alibaba.xxx; import com.alibaba.dubbo.rpc.Protocol; public class XxxProtocol implemenets Protocol { // ... }ExtensionLoad
dubbo擴(kuò)展機(jī)制的實(shí)現(xiàn)核心類(lèi)是ExtensionLoad,幾乎所有擴(kuò)展實(shí)現(xiàn)都在這個(gè)類(lèi)里面。每個(gè)可擴(kuò)展接口的擴(kuò)展實(shí)現(xiàn)類(lèi)和實(shí)現(xiàn)實(shí)例的都管理通過(guò)是ExtensionLoad進(jìn)行,每個(gè)接口維護(hù)一個(gè)單例的ExtensionLoad,所有可擴(kuò)展接口的實(shí)現(xiàn)都維護(hù)在ExtensionLoad中,如下所示:
/** * SPI 類(lèi)和ExtensionLoader映射 */ private static final ConcurrentMap, ExtensionLoader>> EXTENSION_LOADERS = new ConcurrentHashMap , ExtensionLoader>>();
在單例模式中,最典型的實(shí)現(xiàn)就是通過(guò)私有構(gòu)造方法實(shí)現(xiàn)的:
private ExtensionLoader(Class> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
在dubbo擴(kuò)展點(diǎn)實(shí)現(xiàn)過(guò)程中,有幾個(gè)重要的特性需要提前了解一下:
擴(kuò)展點(diǎn)自動(dòng)包裝自動(dòng)包裝擴(kuò)展點(diǎn)的 Wrapper 類(lèi)。ExtensionLoader 在加載擴(kuò)展點(diǎn)時(shí),如果加載到的擴(kuò)展點(diǎn)有拷貝構(gòu)造函數(shù),則判定為擴(kuò)展點(diǎn) Wrapper 類(lèi)。
Wrapper類(lèi)內(nèi)容:
package com.alibaba.xxx; import com.alibaba.dubbo.rpc.Protocol; public class XxxProtocolWrapper implemenets Protocol { Protocol impl; public XxxProtocol(Protocol protocol) { impl = protocol; } // 接口方法做一個(gè)操作后,再調(diào)用extension的方法 public void refer() { //... 一些操作 impl.refer(); // ... 一些操作 } // ... }
Wrapper 類(lèi)同樣實(shí)現(xiàn)了擴(kuò)展點(diǎn)接口,但是 Wrapper 不是擴(kuò)展點(diǎn)的真正實(shí)現(xiàn)。它的用途主要是用于從 ExtensionLoader 返回?cái)U(kuò)展點(diǎn)時(shí),包裝在真正的擴(kuò)展點(diǎn)實(shí)現(xiàn)外。即從 ExtensionLoader 中返回的實(shí)際上是 Wrapper 類(lèi)的實(shí)例,Wrapper 持有了實(shí)際的擴(kuò)展點(diǎn)實(shí)現(xiàn)類(lèi)。這個(gè)是典型的裝飾者模式,即真正的實(shí)現(xiàn)類(lèi)是被包裝在Wrapper之中,Wrapper類(lèi)還做一些其它事情。
擴(kuò)展點(diǎn)自動(dòng)裝配加載擴(kuò)展點(diǎn)時(shí),自動(dòng)注入依賴(lài)的擴(kuò)展點(diǎn)。加載擴(kuò)展點(diǎn)時(shí),擴(kuò)展點(diǎn)實(shí)現(xiàn)類(lèi)的成員如果為其它擴(kuò)展點(diǎn)類(lèi)型,ExtensionLoader 在會(huì)自動(dòng)注入依賴(lài)的擴(kuò)展點(diǎn)。ExtensionLoader 通過(guò)掃描擴(kuò)展點(diǎn)實(shí)現(xiàn)類(lèi)的所有 setter 方法來(lái)判定其成員。即 ExtensionLoader 會(huì)執(zhí)行擴(kuò)展點(diǎn)的拼裝操作。這個(gè)類(lèi)似于Spring的IOC,后面會(huì)專(zhuān)門(mén)介紹。
擴(kuò)展點(diǎn)自適應(yīng)在調(diào)用過(guò)程,自動(dòng)選擇一個(gè)擴(kuò)展實(shí)現(xiàn)執(zhí)行,一個(gè)擴(kuò)展點(diǎn)只允許有一個(gè)自適應(yīng)實(shí)現(xiàn)。dubbo通過(guò)@Adaptive注解標(biāo)定自適應(yīng)實(shí)現(xiàn),這個(gè)注解可以在實(shí)現(xiàn)類(lèi)上,也可以在方法上。比如ExtensionFactory的自適應(yīng)實(shí)現(xiàn)就是通過(guò)在實(shí)現(xiàn)類(lèi)AdaptiveExtensionFactory上加@Adaptive注解實(shí)現(xiàn)的:
@Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { ... }
如 Cluster就是通過(guò)在方法加@Adaptive實(shí)現(xiàn)的:
@SPI(FailoverCluster.NAME) public interface Cluster { /** * Merge the directory invokers to a virtual invoker. * * @param* @param directory * @return cluster invoker * @throws RpcException */ @Adaptive Invoker join(Directory directory) throws RpcException; }
這兩種方式的自適應(yīng)擴(kuò)展類(lèi)的實(shí)現(xiàn)方式也不同,在類(lèi)上加注解是通過(guò)在實(shí)現(xiàn)上標(biāo)識(shí)該類(lèi)為自適應(yīng)實(shí)現(xiàn)類(lèi),而在方法上加注解的,是通過(guò)動(dòng)態(tài)代碼生成自適應(yīng)實(shí)現(xiàn)類(lèi)。
擴(kuò)展點(diǎn)自動(dòng)激活對(duì)于集合類(lèi)擴(kuò)展點(diǎn),比如:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker 等,可以同時(shí)加載多個(gè)實(shí)現(xiàn),此時(shí),可以用自動(dòng)激活來(lái)簡(jiǎn)化配置
在ExtensionLoader中比較重要的公用方法就是這些:
getExtensionLoader
getAdaptiveExtension
getActivateExtension
下面就詳細(xì)剖析一下ExtensionLoader的實(shí)現(xiàn)流程。
獲取ExtensionLoader流程每個(gè)可擴(kuò)展接口對(duì)應(yīng)的ExtensionLoader都是單例,唯一獲取ExtensionLoader對(duì)象的入口就是ExtensionLoader::getExtensionLoader方法,如主要流程圖:
首先通過(guò)ExtensionLoader::getExtensionLoader
public staticExtensionLoader getExtensionLoader(Class type) { 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 loader = (ExtensionLoader ) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader (type)); loader = (ExtensionLoader ) EXTENSION_LOADERS.get(type); } return loader; }
會(huì)校驗(yàn)嘗試獲取Loader的接口是否有@SPI注解,先在緩存中找,如果沒(méi)有緩存,則調(diào)用私有構(gòu)造方法:
private ExtensionLoader(Class> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
這里有個(gè)重要的對(duì)象objectFactory,這個(gè)對(duì)象的作用就是自動(dòng)裝配依賴(lài),也就是IOC,可以看出,除了ObjectFactory本身,所有擴(kuò)展點(diǎn)都有ObjectFactory實(shí)例,這個(gè)也是通過(guò)SPI管理的,它是通過(guò)getAdaptiveExtension()方法獲取,這就是后面要介紹自適應(yīng)擴(kuò)展實(shí)現(xiàn),有關(guān)ObjectFactory的內(nèi)容會(huì)在后面IOC中詳細(xì)分析。
自適應(yīng)擴(kuò)展我們從getAdaptiveExtension()方法切入,這個(gè)方法要完成的任務(wù)就是獲取該擴(kuò)展點(diǎn)的自適應(yīng)實(shí)現(xiàn)實(shí)例,其流程如下圖所示:
主要完成以下工作:
1.檢查自適應(yīng)緩存是否存在。
2.如果緩存未命中,則開(kāi)始自適應(yīng)例構(gòu)建過(guò)程。
3.要構(gòu)建自適應(yīng)實(shí)例,先要有自適應(yīng)的實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)類(lèi)有兩種方式:一種通過(guò)配置文件,一種是通過(guò)是字節(jié)碼的方式動(dòng)態(tài)生成。
配置文件配置的自適應(yīng)類(lèi)通過(guò)在實(shí)現(xiàn)類(lèi)上面加@Adaptive注解,如
..... @Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { }
字節(jié)碼生成的自適應(yīng)實(shí)現(xiàn)類(lèi)是在方法層面@Adaptive注解,如
@SPI("dubbo") public interface Protocol { ....省略代碼 @AdaptiveExporter export(Invoker invoker) throws RpcException; @Adaptive Invoker refer(Class type, URL url) throws RpcException; void destroy(); }
4.優(yōu)先加載配置文件,將自適應(yīng)實(shí)現(xiàn)類(lèi)緩存在cachedAdaptiveClass中,同時(shí)通過(guò)加載配置文件,也將激活實(shí)現(xiàn)緩存在cachedActivates之中,這個(gè)在后面的激活實(shí)現(xiàn)中有用到。將包裝類(lèi)實(shí)例緩存在cachedWrapperClasses??梢院?jiǎn)單一窺加載配置文件的代碼
private void loadFile(Map> extensionClasses, String dir) { String fileName = dir + type.getName(); try { Enumeration urls; ClassLoader classLoader = findClassLoader(); if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { 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) { 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) { Class> clazz = Class.forName(line, true, classLoader); //配置的實(shí)現(xiàn)必須實(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."); } if (clazz.isAnnotationPresent(Adaptive.class)) { //如果是自適應(yīng)實(shí)現(xiàn) if(cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (! cachedAdaptiveClass.equals(clazz)) { //只允許有一個(gè)自適應(yīng)實(shí)現(xiàn)類(lèi) throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName()); } } else { //如果不是自適應(yīng)類(lèi) try { //判斷是不是包裝類(lèi),即是否有接口的構(gòu)造方法 clazz.getConstructor(type); Set > wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet >(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } catch (NoSuchMethodException e) { //不是包裝類(lèi) clazz.getConstructor(); if (name == null || name.length() == 0) { //找到Extension注解 name = findAnnotationName(clazz); if (name == null || name.length() == 0) { //如果Extension注解沒(méi)有默認(rèn)名稱(chēng),則根據(jù)類(lèi)的名稱(chēng)關(guān)系判斷 if (clazz.getSimpleName().length() > type.getSimpleName().length() && clazz.getSimpleName().endsWith(type.getSimpleName())) { //如果實(shí)現(xiàn)類(lèi)和接口有名稱(chēng)上關(guān)系,比如XXImpl則將后面的作為實(shí)現(xiàn)類(lèi)標(biāo)識(shí) 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); } } } String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(names[0], activate); } 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()); } } } } } } } 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); } }
5.加載完配置文件后,檢查緩存的自適應(yīng)實(shí)現(xiàn)類(lèi),若沒(méi)有,則通過(guò)字節(jié)碼技術(shù)生成自適應(yīng)類(lèi)。調(diào)用createAdaptiveExtensionClass()方法,實(shí)際上是通過(guò)拼接class文本,然后通過(guò)compiler編譯文本生成class,這里又是一個(gè)自適應(yīng)的類(lèi)AdaptiveCompiler。
private Class> getAdaptiveExtensionClass() { //先通過(guò)配置文化加載實(shí)現(xiàn)類(lèi),并且識(shí)別自適應(yīng)實(shí)現(xiàn)類(lèi) getExtensionClasses(); if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } //如果沒(méi)有通過(guò)Adaptive注解標(biāo)識(shí)的自適應(yīng)實(shí)現(xiàn)類(lèi),則通過(guò)字節(jié)碼創(chuàng)建 return cachedAdaptiveClass = createAdaptiveExtensionClass(); } ....省略代碼 private Class> createAdaptiveExtensionClass() { String code = createAdaptiveExtensionClassCode(); ClassLoader classLoader = findClassLoader(); com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }
@Adaptive public class AdaptiveCompiler implements Compiler { private static volatile String DEFAULT_COMPILER; public static void setDefaultCompiler(String compiler) { DEFAULT_COMPILER = compiler; } public Class> compile(String code, ClassLoader classLoader) { Compiler compiler; ExtensionLoaderloader = ExtensionLoader.getExtensionLoader(Compiler.class); String name = DEFAULT_COMPILER; // copy reference if (name != null && name.length() > 0) { compiler = loader.getExtension(name); } else { compiler = loader.getDefaultExtension(); } return compiler.compile(code, classLoader); } }
下面是通過(guò)字節(jié)碼動(dòng)態(tài)生成的Protocol接口的自適應(yīng)擴(kuò)展Protocol$Adpative:
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); } public void destroyServer() { throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroyServer() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } }自動(dòng)激活擴(kuò)展
在dubbo中,某些組件可以同時(shí)有多個(gè)實(shí)現(xiàn)同時(shí)加載時(shí),就可以通過(guò)@Activate注解自動(dòng)激活,常見(jiàn)的自動(dòng)激活擴(kuò)展,如過(guò)濾器Filter,有順序要求,提供了三個(gè)排序?qū)傩?,before、after和order。還有一些過(guò)濾條件,主要是通過(guò)分組和key,如Provider和consumer的過(guò)濾邏輯可能就不一樣,Activate的源碼如下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Activate { /** * Group過(guò)濾條件。 */ String[] group() default {}; String[] value() default {}; /** * 排序信息,可以不提供。 */ String[] before() default {}; /** * 排序信息,可以不提供。 */ String[] after() default {}; /** * 排序信息,可以不提供。 */ int order() default 0; }
在ExtensionLoader中,有三個(gè)重載獲取激活擴(kuò)展實(shí)現(xiàn)的方法:
public List
public List
public List
后面兩個(gè)也是通過(guò)調(diào)用第一個(gè)重載方法實(shí)現(xiàn),下面來(lái)分析一下它的源碼:
public ListgetActivateExtension(URL url, String[] values, String group) { List exts = new ArrayList (); List names = values == null ? new ArrayList (0) : Arrays.asList(values); if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) { getExtensionClasses(); for (Map.Entry entry : cachedActivates.entrySet()) { String name = entry.getKey(); Activate activate = entry.getValue(); if (isMatchGroup(group, activate.group())) { T ext = getExtension(name); if (! names.contains(name) && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name) && isActive(activate, url)) { exts.add(ext); } } } Collections.sort(exts, ActivateComparator.COMPARATOR); } List usrs = new ArrayList (); for (int i = 0; i < names.size(); i ++) { String name = names.get(i); if (! name.startsWith(Constants.REMOVE_VALUE_PREFIX) && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)) { if (Constants.DEFAULT_KEY.equals(name)) { if (usrs.size() > 0) { exts.addAll(0, usrs); usrs.clear(); } } else { T ext = getExtension(name); usrs.add(ext); } } } if (usrs.size() > 0) { exts.addAll(usrs); } return exts; }
這個(gè)方法所做的工作無(wú)非就是在之前加載配置時(shí)緩存的cachedActivates中過(guò)濾查詢(xún)符合條件的自動(dòng)激動(dòng)實(shí)例,并根據(jù)@Activate注解中配置的排序規(guī)則排序。
IOC注入在創(chuàng)建自適應(yīng)實(shí)例時(shí),都會(huì)調(diào)用ExtensionLoader的injectExtension方法:
private T createAdaptiveExtension() { try { //傳入自適應(yīng)實(shí)例注入到ExtensionLoader return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e); } } private T injectExtension(T instance) { try { //必須要有對(duì)象工廠 if (objectFactory != null) { for (Method method : instance.getClass().getMethods()) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) { 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) { 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; }
然后我們看到了ExtensionFactory對(duì)象,dubbo中的IOC實(shí)例是通過(guò)ExtensionFactory實(shí)現(xiàn)的,其實(shí)就是檢測(cè)擴(kuò)展實(shí)現(xiàn)類(lèi)有沒(méi)有通過(guò)set方法設(shè)置的屬性,如果有,就通過(guò)ExtensionFactory加載而設(shè)置。
ExtensionFactory的類(lèi)實(shí)現(xiàn)體系:
在構(gòu)造ExtensionLoader對(duì)象時(shí),有個(gè)對(duì)象extensionFactory是必須要?jiǎng)?chuàng)建的,可以看到它就是用自適應(yīng)實(shí)例,而ExtensionFatocry的自適應(yīng)實(shí)例便是AdaptiveExtensionFactory,通過(guò)下面它的源碼,我們可以發(fā)現(xiàn),它維護(hù)了其他非自適應(yīng)擴(kuò)展實(shí)例,其實(shí)也就兩個(gè)SpiExtensionFactory和SpringExtensionFactory。嘗試用這兩個(gè)實(shí)例去加載,加載到便返回。
@Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { private final Listfactories; public AdaptiveExtensionFactory() { ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); List list = new ArrayList (); for (String name : loader.getSupportedExtensions()) { list.add(loader.getExtension(name)); } factories = Collections.unmodifiableList(list); } public T getExtension(Class type, String name) { for (ExtensionFactory factory : factories) { T extension = factory.getExtension(type, name); if (extension != null) { return extension; } } return null; } }
ExtensionFatocry 可以理解為對(duì)象工廠,只不過(guò)這里的對(duì)應(yīng)就是Dubbo中的擴(kuò)展Extension,AdaptiveExtensionFactory可以理解為通用擴(kuò)展實(shí)現(xiàn)獲取的入口,至于具體的獲取方式分為兩種,如果一種是通過(guò)Dubbo 自己的SPI方式加載到的擴(kuò)展,同時(shí)還支持復(fù)用Srping 的方式,可以看看這兩種實(shí)現(xiàn)的代碼便可知:
public class SpiExtensionFactory implements ExtensionFactory { public總結(jié)T getExtension(Class type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { ExtensionLoader loader = ExtensionLoader.getExtensionLoader(type); if (loader.getSupportedExtensions().size() > 0) { return loader.getAdaptiveExtension(); } } return null; } } public class SpringExtensionFactory implements ExtensionFactory { private static final Set contexts = new ConcurrentHashSet (); public static void addApplicationContext(ApplicationContext context) { contexts.add(context); } public static void removeApplicationContext(ApplicationContext context) { contexts.remove(context); } @SuppressWarnings("unchecked") public T getExtension(Class type, String name) { for (ApplicationContext context : contexts) { if (context.containsBean(name)) { Object bean = context.getBean(name); if (type.isInstance(bean)) { return (T) bean; } } } return null; } }
作為貫穿整個(gè)Dubbo設(shè)計(jì)始終的思想,SPI在整個(gè)框架中隨處可見(jiàn),本文圍繞ExtensionLoader擴(kuò)展點(diǎn)機(jī)制,通過(guò)一些dubbo組件擴(kuò)展示例,分析了其核心源碼和流程。希望可以對(duì)于理解Dubbo的擴(kuò)展點(diǎn)乃至dubbo源碼解析過(guò)程中有所幫助,最后總結(jié)幾點(diǎn):
對(duì)于每個(gè)擴(kuò)展點(diǎn),只維護(hù)一個(gè)ExtensionLoad,具體擴(kuò)展實(shí)現(xiàn)類(lèi)和實(shí)例,都是通過(guò)相應(yīng)的ExtensionLoader獲取的。
針對(duì)每個(gè)擴(kuò)展實(shí)現(xiàn)的實(shí)例都是單例的,所以在擴(kuò)展實(shí)現(xiàn)時(shí)應(yīng)保證線程安全。
自適應(yīng)實(shí)現(xiàn)只能有一個(gè),自適應(yīng)實(shí)現(xiàn)類(lèi)獲取有兩種方式,一種是通過(guò)配置文件,這種就是針對(duì)@Adaptive注解在類(lèi)級(jí)別的時(shí),而@Adaptive注解在方法級(jí)別時(shí),自適應(yīng)實(shí)現(xiàn)類(lèi)就需要通過(guò)字符碼動(dòng)態(tài)生成。
自動(dòng)激活擴(kuò)展實(shí)現(xiàn)可以有多個(gè),一般情況下,采用自動(dòng)激動(dòng)方式擴(kuò)展的一般都會(huì)有多個(gè),正因?yàn)橛卸鄠€(gè),自動(dòng)激動(dòng)擴(kuò)展實(shí)現(xiàn)可能有順序性,且可以分組。
參考http://dubbo.apache.org/books...
https://blog.csdn.net/jdluoji...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69282.html
摘要:為了實(shí)現(xiàn)在模塊裝配的時(shí)候,不在模塊里寫(xiě)死代碼,就需要一種服務(wù)發(fā)現(xiàn)機(jī)制。就提供了這樣一種機(jī)制為某個(gè)接口尋找服務(wù)實(shí)現(xiàn),有點(diǎn)類(lèi)似思想,將裝配的控制權(quán)移到代碼之外。即接口文件的全類(lèi)名。五示例遵循上述第一條第點(diǎn),這里為接口文件,其中和為兩個(gè)實(shí)現(xiàn)類(lèi)。 一、Dubbo內(nèi)核 Dubbo內(nèi)核主要包含SPI、AOP、IOC、Compiler。 二、JDK的SPI 1.spi的設(shè)計(jì)目標(biāo): 面向?qū)ο蟮脑O(shè)計(jì)里...
摘要:今天我想聊聊的另一個(gè)很棒的特性就是它的可擴(kuò)展性。的擴(kuò)展機(jī)制在的官網(wǎng)上,描述自己是一個(gè)高性能的框架。接下來(lái)的章節(jié)中我們會(huì)慢慢揭開(kāi)擴(kuò)展機(jī)制的神秘面紗。擴(kuò)展擴(kuò)展點(diǎn)的實(shí)現(xiàn)類(lèi)。的定義在配置文件中可以看到文件中定義了個(gè)的擴(kuò)展實(shí)現(xiàn)。 摘要: 在Dubbo的官網(wǎng)上,Dubbo描述自己是一個(gè)高性能的RPC框架。今天我想聊聊Dubbo的另一個(gè)很棒的特性, 就是它的可擴(kuò)展性。 Dubbo的擴(kuò)展機(jī)制 在Dub...
摘要:在中配置,以配置為例整個(gè),最先使用的地方從里面讀取這個(gè)配置使用接口的中獲取具體的實(shí)現(xiàn)類(lèi)中有兩個(gè)值當(dāng)主線程被外部終止時(shí),會(huì)觸發(fā),執(zhí)行的與方法通知下面的鎖操作,主線程正常走完代碼,并最終停止。 spring是如何啟動(dòng)容器的 常見(jiàn)的一種在本地使用main方法啟動(dòng)spring的方法 public static void main(String[] args) throws Except...
摘要:屬性上篇文章中,提到在獲取擴(kuò)展點(diǎn)接口對(duì)應(yīng)的的時(shí)候,會(huì)執(zhí)行私有構(gòu)造函數(shù)。因?yàn)榇藭r(shí)是,即當(dāng)為時(shí),即我們可以看出,所有非擴(kuò)展點(diǎn)接口都會(huì)執(zhí)行對(duì)應(yīng)的實(shí)例的方法返回一個(gè)實(shí)例,即對(duì)象。 spring是如何獲得容器中管理的類(lèi)的 拿到applicationContext,就可以調(diào)用getBean方法來(lái)獲得Spring的bean對(duì)象了 public class SpringContextUtil impl...
摘要:二注解該注解為了保證在內(nèi)部調(diào)用具體實(shí)現(xiàn)的時(shí)候不是硬編碼來(lái)指定引用哪個(gè)實(shí)現(xiàn),也就是為了適配一個(gè)接口的多種實(shí)現(xiàn),這樣做符合模塊接口設(shè)計(jì)的可插拔原則,也增加了整個(gè)框架的靈活性,該注解也實(shí)現(xiàn)了擴(kuò)展點(diǎn)自動(dòng)裝配的特性。 Dubbo擴(kuò)展機(jī)制SPI 前一篇文章《dubbo源碼解析(一)Hello,Dubbo》是對(duì)dubbo整個(gè)項(xiàng)目大體的介紹,而從這篇文章開(kāi)始,我將會(huì)從源碼來(lái)解讀dubbo再各個(gè)模塊的實(shí)...
閱讀 1529·2021-11-18 10:02
閱讀 1679·2021-09-04 16:40
閱讀 3180·2021-09-01 10:48
閱讀 882·2019-08-30 15:55
閱讀 1860·2019-08-30 15:55
閱讀 1379·2019-08-30 13:05
閱讀 3022·2019-08-30 12:52
閱讀 1632·2019-08-30 11:24