SPI,Service Provider Interface,java中提供的一種使程序可擴展的方式,系統定義好接口規範,供其餘服務提供方實現,服務提供方將本身jar包META-INF/services下新建一個以接口全名稱定義的文件,裏面內容寫上本身服務的實現的類名,每一行表明一個實現,服務使用方能夠經過ServiceLoader.load加載全部的服務,而後判斷能夠使用的服務。具體能夠參考referencejava
dubbo也使用了相似Java中的SPI機制,不過服務發現等都是dubbo本身實現的。緩存
dubbo中的ServiceLoader:com.alibaba.dubbo.common.extension.ExtensionLoaderapp
dubbo中的擴展聲明配置文件的位置:ide
private static final String SERVICES_DIRECTORY = "META-INF/services/"; private static final String DUBBO_DIRECTORY = "META-INF/dubbo/"; private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
dubbo中的擴展聲明配置文件的內容(好比com.alibaba.dubbo.rpc.Protocol):url
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper mock=com.alibaba.dubbo.rpc.support.MockProtocol
文件裏面都是name=value的格式,name就是該擴展的名稱,value是實現的擴展類的全限定名城,以#開始的都是註釋.net
dubbo爲了支持可擴展的SPI機制,有三個相關重要的註解code
/** * 寫在可擴展的接口上面,代表該接口是支持SPI擴展的,有一個屬性value表示默認的擴展類名稱(就是配置文件中的name) * 好比:Protocol上的@SPI("dubbo"),說明默認的Protocol的擴展是name爲dubbo的擴展, * 查找配置文件META_INFO/dubbo.internal/com.alibaba.dubbo.rpc.Protocol * 對應的擴展類是com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol */ com.alibaba.dubbo.common.extension.SPI /** * 寫在類上面表示該類是默認的adapter,寫在方法上表示該方法須要被adapt, * 屬性value表示須要被adapt成的擴展的key(在url中配 置的key) * 好比com.alibaba.dubbo.common.threadpool.ThreadPool#getExecutor方法上面的註解:@Adaptive({Constants.THREADPOOL_KEY}) * 表示該方法返回的Executor是url中key是threadpool指定的擴展,若是url中沒有配置則使用默認的擴展 */ com.alibaba.dubbo.common.extension.Adaptive /** * 指定默認激活的擴展,屬性group和value用來過濾(判斷何時激活) * 好比:com.alibaba.dubbo.rpc.filter.ExceptionFilter上的@Activate(group = Constants.PROVIDER) * 該filter只有在provider一側的時候纔會被激活,也就是服務提供方的時候纔會使用該filter */ com.alibaba.dubbo.common.extension.Activate
dubbo獲取擴展的方式都是先獲取extensionLoader,而後經過loader去加載對應的擴展對象
獲取對應的extensionLoader的方法,每一個擴展接口都有本身的extensionLoader實例,獲取以後緩存在EXTENSION_LOADERS中blog
com.alibaba.dubbo.common.extension.ExtensionLoader#getExtensionLoader
排序
獲取擴展的方法主要有:
// 獲取適配後的擴展 com.alibaba.dubbo.common.extension.ExtensionLoader#getAdaptiveExtension // 獲取active的擴展 com.alibaba.dubbo.common.extension.ExtensionLoader#getActivateExtension // 獲取默認配置的擴展 com.alibaba.dubbo.common.extension.ExtensionLoader#getDefaultExtension // 根據name(配置文件中的key)獲取擴展 com.alibaba.dubbo.common.extension.ExtensionLoader#getExtension
加載擴展的調用路徑
com.alibaba.dubbo.common.extension.ExtensionLoader#getExtensionClasses com.alibaba.dubbo.common.extension.ExtensionLoader#loadExtensionClasses com.alibaba.dubbo.common.extension.ExtensionLoader#loadFile
獲取指定擴展接口的adaptive類,能夠是動態生成的,也能夠使用註解@Adaptivate指定
該方法會生成一個實現了擴展接口的adaptive類,該類實現了接口的全部方法,對於沒有Adaptive註解的方法註解拋出UnsupportedOperationException,若是有該註解會從url中找到須要適配到的擴展,而後調用適配到的擴展的對應的方法。好比ProxyFactory接口的getProxy方法,默認會去url中找key爲proxy對應的value,若是沒有配置proxy的話,默認的value是javassist,而後根據該value加載對應的擴展,默認擴展JavassistProxyFactory,而後調用JavassistProxyFactory#getProxy
下面是動態生成的接口com.alibaba.dubbo.rpc.ProxyFactory的適配類
import com.alibaba.dubbo.common.extension.ExtensionLoader; public class ProxyFactory$Adpative implements com.alibaba.dubbo.rpc.ProxyFactory { public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common .URL arg2) throws com.alibaba.dubbo.rpc.RpcException { if (arg2 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg2; String extName = url.getParameter("proxy", "javassist"); if (extName == null) throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])"); com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader( com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName); return extension.getInvoker(arg0, arg1, arg2); } public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException { 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.getParameter("proxy", "javassist"); if (extName == null) throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])"); com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader( com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName); return extension.getProxy(arg0); } }
該方法主要獲取兩種擴展:
找到的全部擴展是有順序的,若是用戶有配置按照用戶配置的前後順序(默認activate的擴展的排序規則ActivateComparator)
從cachedActivates中查找的主要步驟是:
查找指定name擴展的步驟是:
獲取默認的擴展,也是先加載該接口全部的擴展,這個過程當中會將SPI註解配置了value——也就是默認的使用的擴展,賦值給cachedDefaultName,而後調用getExtension加載該nam對應的擴展
根據給定的name來獲取擴展的class,返回對應的實例對象
根據名稱獲取擴展
dubbo按照java中的SPI機制來保證擴展性,按照約定將對應的配置文件放在指定的目錄下,經過讀取配置件的方式來獲取接口的擴展實現,能夠使用動態編譯的方法能夠動態獲取擴展的適配類。將全部實例化的類緩存起來能夠保證單例並且加快速度,同時dubbo有簡單的屬性裝配功能,在實例化擴展的時候會將屬性也爲擴展接口的字段進行注入。