摘要:要構建自適應實例,先要有自適應的實現類,實現類有兩種方式一種通過配置文件,一種是通過是字節碼的方式動態生成。
SPI機制
SPI,即(service provider interface)機制,有很多組件的實現,如日志、數據庫訪問等都是采用這樣的方式,一般通用組件為了提升可擴展性,基于接口編程,將操作接口形成標準規范,但是可以開放多種擴展實現,這種做法也符合開閉設計原則,使組件具有可插撥特性。不同的廠商或組織可以基于規范推出自己的實現,只需要在自己的jar包中通過配置文件和相應的實現類即可以實現擴展。甚至開發者自己也可以很方便對框架進行定制化實現。
JDK SPI介紹JDK實現spi服務查找: ServiceLoader。
舉個例子:
首先定義下示例接口
package com.example; public interface Spi { booleanisSupport(String name); String sayHello(); }
ServiceLoader會遍歷所有jar查找META-INF/services/com.example.Spi文件
A廠商提供實現
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文件內容為:
com.a.example.SpiAImpl #廠商A的spi實現全路徑類名
B廠商提供實現
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文件內容為:
com.b.example.SpiBImpl #廠商B的spi實現全路徑類名
ServiceLoader.load(Spi.class)讀取廠商A、B提供jar包中的文件,ServiceLoader實現了Iterable接口可通過while for循環語句遍歷出所有實現。
一個接口多種實現,就如策略模式一樣提供了策略的實現,但是沒有提供策略的選擇, 使用方可以根據isSupport方法根據業務傳入廠商名來選擇具體的廠商。
public class SpiFactory { //讀取配置獲取所有實現 privatestatic ServiceLoader spiLoader = ServiceLoader.load(Spi.class); //根據名字選取對應實現 publicstatic Spi getSpi(String name) { for(Spi spi : spiLoader) { if(spi.isSupport(name) ) { returnspi; } } returnnull; } }Duddo SPI
Dubbo 改進了 JDK 標準的 SPI 的以下問題:
JDK 標準的 SPI 會一次性實例化擴展點所有實現,如果有擴展實現初始化很耗時,但如果沒用上也加載,會很浪費資源。
如果擴展點加載失敗,連擴展點的名稱都拿不到了。
增加了對擴展點 IoC 和 AOP 的支持,一個擴展點可以直接 setter 注入其它擴展點。
示例在擴展類的jar包內,放置擴展點配置文件 META-INF/dubbo/接口全限定名,內容為:配置名=擴展實現類全限定名,多個實現類用換行符分隔。
以擴展 Dubbo 的協議為例,在協議的實現 jar 包內放置文本文件:META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol,內容為:
xxx=com.alibaba.xxx.XxxProtocol
實現類內容:
package com.alibaba.xxx; import com.alibaba.dubbo.rpc.Protocol; public class XxxProtocol implemenets Protocol { // ... }ExtensionLoad
dubbo擴展機制的實現核心類是ExtensionLoad,幾乎所有擴展實現都在這個類里面。每個可擴展接口的擴展實現類和實現實例的都管理通過是ExtensionLoad進行,每個接口維護一個單例的ExtensionLoad,所有可擴展接口的實現都維護在ExtensionLoad中,如下所示:
/** * SPI 類和ExtensionLoader映射 */ private static final ConcurrentMap, ExtensionLoader>> EXTENSION_LOADERS = new ConcurrentHashMap , ExtensionLoader>>();
在單例模式中,最典型的實現就是通過私有構造方法實現的:
private ExtensionLoader(Class> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
在dubbo擴展點實現過程中,有幾個重要的特性需要提前了解一下:
擴展點自動包裝自動包裝擴展點的 Wrapper 類。ExtensionLoader 在加載擴展點時,如果加載到的擴展點有拷貝構造函數,則判定為擴展點 Wrapper 類。
Wrapper類內容:
package com.alibaba.xxx; import com.alibaba.dubbo.rpc.Protocol; public class XxxProtocolWrapper implemenets Protocol { Protocol impl; public XxxProtocol(Protocol protocol) { impl = protocol; } // 接口方法做一個操作后,再調用extension的方法 public void refer() { //... 一些操作 impl.refer(); // ... 一些操作 } // ... }
Wrapper 類同樣實現了擴展點接口,但是 Wrapper 不是擴展點的真正實現。它的用途主要是用于從 ExtensionLoader 返回擴展點時,包裝在真正的擴展點實現外。即從 ExtensionLoader 中返回的實際上是 Wrapper 類的實例,Wrapper 持有了實際的擴展點實現類。這個是典型的裝飾者模式,即真正的實現類是被包裝在Wrapper之中,Wrapper類還做一些其它事情。
擴展點自動裝配加載擴展點時,自動注入依賴的擴展點。加載擴展點時,擴展點實現類的成員如果為其它擴展點類型,ExtensionLoader 在會自動注入依賴的擴展點。ExtensionLoader 通過掃描擴展點實現類的所有 setter 方法來判定其成員。即 ExtensionLoader 會執行擴展點的拼裝操作。這個類似于Spring的IOC,后面會專門介紹。
擴展點自適應在調用過程,自動選擇一個擴展實現執行,一個擴展點只允許有一個自適應實現。dubbo通過@Adaptive注解標定自適應實現,這個注解可以在實現類上,也可以在方法上。比如ExtensionFactory的自適應實現就是通過在實現類AdaptiveExtensionFactory上加@Adaptive注解實現的:
@Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { ... }
如 Cluster就是通過在方法加@Adaptive實現的:
@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; }
這兩種方式的自適應擴展類的實現方式也不同,在類上加注解是通過在實現上標識該類為自適應實現類,而在方法上加注解的,是通過動態代碼生成自適應實現類。
擴展點自動激活對于集合類擴展點,比如:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker 等,可以同時加載多個實現,此時,可以用自動激活來簡化配置
在ExtensionLoader中比較重要的公用方法就是這些:
getExtensionLoader
getAdaptiveExtension
getActivateExtension
下面就詳細剖析一下ExtensionLoader的實現流程。
獲取ExtensionLoader流程每個可擴展接口對應的ExtensionLoader都是單例,唯一獲取ExtensionLoader對象的入口就是ExtensionLoader::getExtensionLoader方法,如主要流程圖:
首先通過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; }
會校驗嘗試獲取Loader的接口是否有@SPI注解,先在緩存中找,如果沒有緩存,則調用私有構造方法:
private ExtensionLoader(Class> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
這里有個重要的對象objectFactory,這個對象的作用就是自動裝配依賴,也就是IOC,可以看出,除了ObjectFactory本身,所有擴展點都有ObjectFactory實例,這個也是通過SPI管理的,它是通過getAdaptiveExtension()方法獲取,這就是后面要介紹自適應擴展實現,有關ObjectFactory的內容會在后面IOC中詳細分析。
自適應擴展我們從getAdaptiveExtension()方法切入,這個方法要完成的任務就是獲取該擴展點的自適應實現實例,其流程如下圖所示:
主要完成以下工作:
1.檢查自適應緩存是否存在。
2.如果緩存未命中,則開始自適應例構建過程。
3.要構建自適應實例,先要有自適應的實現類,實現類有兩種方式:一種通過配置文件,一種是通過是字節碼的方式動態生成。
配置文件配置的自適應類通過在實現類上面加@Adaptive注解,如
..... @Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { }
字節碼生成的自適應實現類是在方法層面@Adaptive注解,如
@SPI("dubbo") public interface Protocol { ....省略代碼 @AdaptiveExporter export(Invoker invoker) throws RpcException; @Adaptive Invoker refer(Class type, URL url) throws RpcException; void destroy(); }
4.優先加載配置文件,將自適應實現類緩存在cachedAdaptiveClass中,同時通過加載配置文件,也將激活實現緩存在cachedActivates之中,這個在后面的激活實現中有用到。將包裝類實例緩存在cachedWrapperClasses。可以簡單一窺加載配置文件的代碼
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); //配置的實現必須實現該接口 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)) { //如果是自適應實現 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()); } } else { //如果不是自適應類 try { //判斷是不是包裝類,即是否有接口的構造方法 clazz.getConstructor(type); Set > wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet >(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } catch (NoSuchMethodException e) { //不是包裝類 clazz.getConstructor(); if (name == null || name.length() == 0) { //找到Extension注解 name = findAnnotationName(clazz); if (name == null || name.length() == 0) { //如果Extension注解沒有默認名稱,則根據類的名稱關系判斷 if (clazz.getSimpleName().length() > type.getSimpleName().length() && clazz.getSimpleName().endsWith(type.getSimpleName())) { //如果實現類和接口有名稱上關系,比如XXImpl則將后面的作為實現類標識 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.加載完配置文件后,檢查緩存的自適應實現類,若沒有,則通過字節碼技術生成自適應類。調用createAdaptiveExtensionClass()方法,實際上是通過拼接class文本,然后通過compiler編譯文本生成class,這里又是一個自適應的類AdaptiveCompiler。
private Class> getAdaptiveExtensionClass() { //先通過配置文化加載實現類,并且識別自適應實現類 getExtensionClasses(); if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } //如果沒有通過Adaptive注解標識的自適應實現類,則通過字節碼創建 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); } }
下面是通過字節碼動態生成的Protocol接口的自適應擴展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!"); } }自動激活擴展
在dubbo中,某些組件可以同時有多個實現同時加載時,就可以通過@Activate注解自動激活,常見的自動激活擴展,如過濾器Filter,有順序要求,提供了三個排序屬性,before、after和order。還有一些過濾條件,主要是通過分組和key,如Provider和consumer的過濾邏輯可能就不一樣,Activate的源碼如下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Activate { /** * Group過濾條件。 */ String[] group() default {}; String[] value() default {}; /** * 排序信息,可以不提供。 */ String[] before() default {}; /** * 排序信息,可以不提供。 */ String[] after() default {}; /** * 排序信息,可以不提供。 */ int order() default 0; }
在ExtensionLoader中,有三個重載獲取激活擴展實現的方法:
public List
public List
public List
后面兩個也是通過調用第一個重載方法實現,下面來分析一下它的源碼:
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; }
這個方法所做的工作無非就是在之前加載配置時緩存的cachedActivates中過濾查詢符合條件的自動激動實例,并根據@Activate注解中配置的排序規則排序。
IOC注入在創建自適應實例時,都會調用ExtensionLoader的injectExtension方法:
private T createAdaptiveExtension() { try { //傳入自適應實例注入到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 { //必須要有對象工廠 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對象,dubbo中的IOC實例是通過ExtensionFactory實現的,其實就是檢測擴展實現類有沒有通過set方法設置的屬性,如果有,就通過ExtensionFactory加載而設置。
ExtensionFactory的類實現體系:
在構造ExtensionLoader對象時,有個對象extensionFactory是必須要創建的,可以看到它就是用自適應實例,而ExtensionFatocry的自適應實例便是AdaptiveExtensionFactory,通過下面它的源碼,我們可以發現,它維護了其他非自適應擴展實例,其實也就兩個SpiExtensionFactory和SpringExtensionFactory。嘗試用這兩個實例去加載,加載到便返回。
@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 可以理解為對象工廠,只不過這里的對應就是Dubbo中的擴展Extension,AdaptiveExtensionFactory可以理解為通用擴展實現獲取的入口,至于具體的獲取方式分為兩種,如果一種是通過Dubbo 自己的SPI方式加載到的擴展,同時還支持復用Srping 的方式,可以看看這兩種實現的代碼便可知:
public class SpiExtensionFactory implements ExtensionFactory { public總結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; } }
作為貫穿整個Dubbo設計始終的思想,SPI在整個框架中隨處可見,本文圍繞ExtensionLoader擴展點機制,通過一些dubbo組件擴展示例,分析了其核心源碼和流程。希望可以對于理解Dubbo的擴展點乃至dubbo源碼解析過程中有所幫助,最后總結幾點:
對于每個擴展點,只維護一個ExtensionLoad,具體擴展實現類和實例,都是通過相應的ExtensionLoader獲取的。
針對每個擴展實現的實例都是單例的,所以在擴展實現時應保證線程安全。
自適應實現只能有一個,自適應實現類獲取有兩種方式,一種是通過配置文件,這種就是針對@Adaptive注解在類級別的時,而@Adaptive注解在方法級別時,自適應實現類就需要通過字符碼動態生成。
自動激活擴展實現可以有多個,一般情況下,采用自動激動方式擴展的一般都會有多個,正因為有多個,自動激動擴展實現可能有順序性,且可以分組。
參考http://dubbo.apache.org/books...
https://blog.csdn.net/jdluoji...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/69282.html
摘要:為了實現在模塊裝配的時候,不在模塊里寫死代碼,就需要一種服務發現機制。就提供了這樣一種機制為某個接口尋找服務實現,有點類似思想,將裝配的控制權移到代碼之外。即接口文件的全類名。五示例遵循上述第一條第點,這里為接口文件,其中和為兩個實現類。 一、Dubbo內核 Dubbo內核主要包含SPI、AOP、IOC、Compiler。 二、JDK的SPI 1.spi的設計目標: 面向對象的設計里...
摘要:今天我想聊聊的另一個很棒的特性就是它的可擴展性。的擴展機制在的官網上,描述自己是一個高性能的框架。接下來的章節中我們會慢慢揭開擴展機制的神秘面紗。擴展擴展點的實現類。的定義在配置文件中可以看到文件中定義了個的擴展實現。 摘要: 在Dubbo的官網上,Dubbo描述自己是一個高性能的RPC框架。今天我想聊聊Dubbo的另一個很棒的特性, 就是它的可擴展性。 Dubbo的擴展機制 在Dub...
摘要:在中配置,以配置為例整個,最先使用的地方從里面讀取這個配置使用接口的中獲取具體的實現類中有兩個值當主線程被外部終止時,會觸發,執行的與方法通知下面的鎖操作,主線程正常走完代碼,并最終停止。 spring是如何啟動容器的 常見的一種在本地使用main方法啟動spring的方法 public static void main(String[] args) throws Except...
摘要:屬性上篇文章中,提到在獲取擴展點接口對應的的時候,會執行私有構造函數。因為此時是,即當為時,即我們可以看出,所有非擴展點接口都會執行對應的實例的方法返回一個實例,即對象。 spring是如何獲得容器中管理的類的 拿到applicationContext,就可以調用getBean方法來獲得Spring的bean對象了 public class SpringContextUtil impl...
摘要:二注解該注解為了保證在內部調用具體實現的時候不是硬編碼來指定引用哪個實現,也就是為了適配一個接口的多種實現,這樣做符合模塊接口設計的可插拔原則,也增加了整個框架的靈活性,該注解也實現了擴展點自動裝配的特性。 Dubbo擴展機制SPI 前一篇文章《dubbo源碼解析(一)Hello,Dubbo》是對dubbo整個項目大體的介紹,而從這篇文章開始,我將會從源碼來解讀dubbo再各個模塊的實...
閱讀 1526·2021-11-18 10:02
閱讀 1671·2021-09-04 16:40
閱讀 3178·2021-09-01 10:48
閱讀 878·2019-08-30 15:55
閱讀 1857·2019-08-30 15:55
閱讀 1377·2019-08-30 13:05
閱讀 3020·2019-08-30 12:52
閱讀 1630·2019-08-30 11:24