SPI,即(service provider interface)機制,有不少組件的實現,如日誌、數據庫訪問等都是採用這樣的方式,通常通用組件爲了提高可擴展性,基於接口編程,將操做接口造成標準規範,可是能夠開放多種擴展實現,這種作法也符合開閉設計原則,使組件具備可插撥特性。不一樣的廠商或組織能夠基於規範推出本身的實現,只須要在本身的jar包中經過配置文件和相應的實現類便可以實現擴展。甚至開發者本身也能夠很方便對框架進行定製化實現。java
JDK實現spi服務查找: ServiceLoader。
舉個例子:
首先定義下示例接口數據庫
package com.example; public interface Spi { booleanisSupport(String name); String sayHello(); }
ServiceLoader會遍歷全部jar查找META-INF/services/com.example.Spi文件apache
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廠商提供實現app
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實現全路徑類名ide
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; } }
Dubbo 改進了 JDK 標準的 SPI 的如下問題:
在擴展類的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 { // ... }
dubbo擴展機制的實現核心類是ExtensionLoad,幾乎全部擴展實現都在這個類裏面。每一個可擴展接口的擴展實現類和實現實例的都管理經過是ExtensionLoad進行,每一個接口維護一個單例的ExtensionLoad,全部可擴展接口的實現都維護在ExtensionLoad中,以下所示:
/** * SPI 類和ExtensionLoader映射 */ private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, 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 <T> * @param directory * @return cluster invoker * @throws RpcException */ @Adaptive <T> Invoker<T> join(Directory<T> directory) throws RpcException; }
這兩種方式的自適應擴展類的實現方式也不一樣,在類上加註解是經過在實現上標識該類爲自適應實現類,而在方法上加註解的,是經過動態代碼生成自適應實現類。
對於集合類擴展點,好比:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker 等,能夠同時加載多個實現,此時,能夠用自動激活來簡化配置
在ExtensionLoader中比較重要的公用方法就是這些:
下面就詳細剖析一下ExtensionLoader的實現流程。
每一個可擴展接口對應的ExtensionLoader都是單例,惟一獲取ExtensionLoader對象的入口就是ExtensionLoader::getExtensionLoader方法,如主要流程圖:
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!"); } if(!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) 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()方法切入,這個方法要完成的任務就是獲取該擴展點的自適應實現實例,其流程以下圖所示:
主要完成如下工做:
配置文件配置的自適應類經過在實現類上面加@Adaptive註解,如
..... @Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { }
字節碼生成的自適應實現類是在方法層面@Adaptive註解,如
@SPI("dubbo") public interface Protocol { ....省略代碼 @Adaptive <T> Exporter<T> export(Invoker<T> invoker) throws RpcException; @Adaptive <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException; void destroy(); }
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) { String fileName = dir + type.getName(); try { Enumeration<java.net.URL> 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<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) { //找到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); } }
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; ExtensionLoader<Compiler> loader = 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<T> getActivateExtension(URL url, String[] values, String group) { List<T> exts = new ArrayList<T>(); List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values); if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) { getExtensionClasses(); for (Map.Entry<String, Activate> 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<T> usrs = new ArrayList<T>(); 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註解中配置的排序規則排序。
在建立自適應實例時,都會調用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 List<ExtensionFactory> factories; public AdaptiveExtensionFactory() { ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); List<ExtensionFactory> list = new ArrayList<ExtensionFactory>(); for (String name : loader.getSupportedExtensions()) { list.add(loader.getExtension(name)); } factories = Collections.unmodifiableList(list); } public <T> T getExtension(Class<T> 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> T getExtension(Class<T> type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type); if (loader.getSupportedExtensions().size() > 0) { return loader.getAdaptiveExtension(); } } return null; } } public class SpringExtensionFactory implements ExtensionFactory { private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>(); public static void addApplicationContext(ApplicationContext context) { contexts.add(context); } public static void removeApplicationContext(ApplicationContext context) { contexts.remove(context); } @SuppressWarnings("unchecked") public <T> T getExtension(Class<T> 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源碼解析過程當中有所幫助,最後總結幾點:
http://dubbo.apache.org/books...
https://blog.csdn.net/jdluoji...