1. 程式人生 > >Dubbo SPI

Dubbo SPI

獲取 meta class filter 模式 引用 bsp 函數 adapt

Dubbo SPI是Dubbo留的擴展點

例如Dubbo的 Protocol 。 ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()

Dubbo SPI 加載文件

loadFile(extensionClasses, "META-INF/dubbo/internal/");
loadFile(extensionClasses, "META-INF/dubbo/");
loadFile(extensionClasses, "META-INF/services/");

Dubbo SPI:(核心類 ExtensionLoader )


【加載SPI文件】
它是按文件去加載的,每個類都有自己的 ExtensionLoader ,保存了相應的SPI文件裏面的信息:
[1]. 類上有 @Adaptive 放在 cachedAdaptiveClass 中
[2]. 類中存在只有一個參數的構造函數,且參數就是這個類本身,則放在 cachedWrapperClasses 中
[3].
類上有 @Activate,將 @Activate 放在 cachedActivates<String:NAME, Activate> 中
將類放在 cachedNames<Class, String:NAME> 和 cachedClasses<String:NAME, Class> 中

舉例,如:META-INF\dubbo\internal\com.alibaba.dubbo.rpc.ProxyFactory
NAME | Class
stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory
javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory

StubProxyFactoryWrapper 上沒有註解,但是有一個參數的構造函數,且參數為 ProxyFactory ,故在 ProxyFactory 相應的 ExtensionLoader 中,cachedWrapperClasses 存放了 StubProxyFactoryWrapper

JdkProxyFactory 上沒有註釋,只有默認的構造函數,所以走分支[3],將 JdkProxyFactory 放在 cachedNames<Class=JdkProxyFactory, String:NAME=jdk> 和 cachedClasses<String:NAME=jdk, class="JdkProxyFactory"> 中
JavassistProxyFactory 上沒有註釋,只有默認的構造函數,所以走分支[3],將 JdkProxyFactory 放在 cachedNames<Class=JavassistProxyFactory, String:NAME=javassist> 和 cachedClasses<String:NAME=javassist, class="JavassistProxyFactory"> 中

舉例,如:META-INF\dubbo\internal\com.alibaba.dubbo.common.extension.ExtensionFactory
NAME | Class
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory

AdaptiveExtensionFactory 上有 @Adaptive ,放在 cachedAdaptiveClass 中。(它是一個適配工廠類)
SpiExtensionFactory 不滿足[1]、[2],也沒有 @Activate ,放在 cachedNames<Class, String:spi> 和 cachedClasses<String:spi, Class> 中
SpringExtensionFactory 不滿足[1]、[2],也沒有 @Activate ,放在 cachedNames<Class, String:spring> 和 cachedClasses<String:spring, Class> 中
附:
AdaptiveExtensionFactory 是一個適配工廠類,通過它可以去找到 spi,spring 對應的 ExtensionFactory

ExtensionLoader.getExtensionLoader(X).getAdaptiveExtension():獲取 X 適配的擴展(X如:Protocol,ProxyFactory)
1. 如果存在 cachedAdaptiveClass ,就返回 cachedAdaptiveClass。(一般是一個適配工廠類)
2. 否則通過 javassist 動態生成一個代理類,然後賦值給 cachedAdaptiveClass 返回
動態生成的這個代理類,會重寫 X 中帶有 @Adaptive 的方法,其他的方法默認拋出異常
重寫的方法中通常都會通過 ExtensionLoader.getExtensionLoader(X).getExtension(extName) 來獲取相應的擴展實現
綜上,getAdaptiveExtension() 就是獲取適配器。適配器作用:獲取與 X 適配的擴展

ExtensionLoader.getExtensionLoader(X).getExtension(extName):根據擴展名 extName 獲取 X 對應的擴展實現
1. 從 cachedClasses 中獲取擴展的實現類,並 clazz.newInstance()
2. 利用裝飾模式,將 cachedWrapperClasses 包裹在 instance 上

Q&A:
1. Dubbo 的 Filter 是什麽時候生效的?
在 com.alibaba.dubbo.rpc.Protocol 的 SPI 文件中,有一個 filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper。
那麽 Dubbo 在服務暴露 or 引用時,會去先調用 ProtocolFilterWrapper.export(Invoker),這個時候就會將適配的 Filter 裝飾在 Invoker 上

2. Dubbo 服務暴露時,何時做的服務註冊?
ServiceConfig.doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) 會去做服務註冊。
protocol.export(invoker) --> RegisteryProtocol.export() --> DubboProtocol.export()
之所以服務註冊時,會去調用 DubboProtocol.export(),這歸功於 listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper。
ProtocolListenerWrapper 會調用 DubboProtocol.export()

Dubbo SPI