1、面試題
dubbo的spi思想是什么?
2、面試官心里分析
繼續深入問唄,前面一些基礎性的東西問完了,確定你應該都ok了解dubbo的一些基本東西,那么問個稍微難一點點的問題,就是spi,先問問你spi是啥?然后問問你dubbo的spi是怎么實現的?
其實就是看看你對dubbo的掌握如何
3、面試題剖析
spi,簡單來說,就是service provider interface,說白了是什么意思呢,比如你有個接口,現在這個接口有3個實現類,那么在系統運行的時候對這個接口到底選擇哪個實現類呢?這就需要spi了,需要根據指定的配置或者是默認的配置,去找到對應的實現類加載進來,然后用這個實現類的實例對象。
接口A -> 實現A1,實現A2,實現A3
配置一下,接口A = 實現A2
在系統實際運行的時候,會加載你的配置,用實現A2實例化一個對象來提供服務
比如說你要通過jar包的方式給某個接口提供實現,然后你就在自己jar包的META-INF/services/目錄下放一個跟接口同名的文件,里面指定接口的實現里是自己這個jar包里的某個類。ok了,別人用了一個接口,然后用了你的jar包,就會在運行的時候通過你的jar包的那個文件找到這個接口該用哪個實現類。
這是jdk提供的一個功能。
比如說你有個工程A,有個接口A,接口A在工程A里是沒有實現類的 -> 系統在運行的時候,怎么給接口A選擇一個實現類呢?
你就可以自己搞一個jar包,META-INF/services/,放上一個文件,文件名就是接口名,接口A,接口A的實現類=com.zhss.service.實現類A2。讓工程A來依賴你的這個jar包,然后呢在系統運行的時候,工程A跑起來,對接口A,就會掃描自己依賴的所有的jar包,在每個jar里找找,有沒有META-INF/services文件夾,如果有,在里面找找,有沒有接口A這個名字的文件,如果有在里面找一下你指定的接口A的實現是你的jar包里的哪個類?
SPI機制,一般來說用在哪兒?插件擴展的場景,比如說你開發的是一個給別人使用的開源框架,如果你想讓別人自己寫個插件,插到你的開源框架里面來,擴展某個功能。
經典的思想體現,大家平時都在用,比如說jdbc
java定義了一套jdbc的接口,但是java是沒有提供jdbc的實現類
但是實際上項目跑的時候,要使用jdbc接口的哪些實現類呢?一般來說,我們要根據自己使用的數據庫,比如msyql,你就將mysql-jdbc-connector.jar,引入進來;oracle,你就將oracle-jdbc-connector.jar,引入進來。
在系統跑的時候,碰到你使用jdbc的接口,他會在底層使用你引入的那個jar中提供的實現類
但是dubbo也用了spi思想,不過沒有用jdk的spi機制,是自己實現的一套spi機制。
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Protocol接口,dubbo要判斷一下,在系統運行的時候,應該選用這個Protocol接口的哪個實現類來實例化對象來使用呢?
他會去找一個你配置的Protocol,他就會將你配置的Protocol實現類,加載到jvm中來,然后實例化對象,就用你的那個Protocol實現類就可以了
微內核,可插拔,大量的組件,Protocol負責rpc調用的東西,你可以實現自己的rpc調用組件,實現Protocol接口,給自己的一個實現類即可。
這行代碼就是dubbo里大量使用的,就是對很多組件,都是保留一個接口和多個實現,然后在系統運行的時候動態根據配置去找到對應的實現類。如果你沒配置,那就走默認的實現好了,沒問題。
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
在dubbo自己的jar里,在/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件中:
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
所以說,這就看到了dubbo的spi機制默認是怎么玩兒的了,其實就是Protocol接口,@SPI(“dubbo”)說的是,通過SPI機制來提供實現類,實現類是通過dubbo作為默認key去配置文件里找到的,配置文件名稱與接口全限定名一樣的,通過dubbo作為key可以找到默認的實現了就是com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol。
dubbo的默認網絡通信協議,就是dubbo協議,用的DubboProtocol
如果想要動態替換掉默認的實現類,需要使用@Adaptive接口,Protocol接口中,有兩個方法加了@Adaptive注解,就是說那倆接口會被代理實現。
啥意思呢?
比如這個Protocol接口搞了倆@Adaptive注解標注了方法,在運行的時候會針對Protocol生成代理類,這個代理類的那倆方法里面會有代理代碼,代理代碼會在運行的時候動態根據url中的protocol來獲取那個key,默認是dubbo,你也可以自己指定,你如果指定了別的key,那么就會獲取別的實現類的實例了。
通過這個url中的參數不通,就可以控制動態使用不同的組件實現類
好吧,那下面來說說怎么來自己擴展dubbo中的組件
自己寫個工程,要是那種可以打成jar包的,里面的src/main/resources目錄下,搞一個META-INF/services,里面放個文件叫:com.alibaba.dubbo.rpc.Protocol,文件里搞一個my=com.zhss.MyProtocol。自己把jar弄到nexus私服里去。
然后自己搞一個dubbo provider工程,在這個工程里面依賴你自己搞的那個jar,然后在spring配置文件里給個配置:
<dubbo:protocol name=”my” port=”20000” />
這個時候provider啟動的時候,就會加載到我們jar包里的my=com.zhss.MyProtocol這行配置里,接着會根據你的配置使用你定義好的MyProtocol了,這個就是簡單說明一下,你通過上述方式,可以替換掉大量的dubbo內部的組件,就是扔個你自己的jar包,然后配置一下即可。
dubbo里面提供了大量的類似上面的擴展點,就是說,你如果要擴展一個東西,只要自己寫個jar,讓你的consumer或者是provider工程,依賴你的那個jar,在你的jar里指定目錄下配置好接口名稱對應的文件,里面通過key=實現類。
然后對對應的組件,用類似<dubbo:protocol>用你的哪個key對應的實現類來實現某個接口,你可以自己去擴展dubbo的各種功能,提供你自己的實現。
dubbo的SPI原理:

