面試題
dubbo 的 spi 思想是什么?
面試官心理分析
繼續(xù)深入問唄财剖,前面一些基礎(chǔ)性的東西問完了悠夯,確定你應(yīng)該都 ok,了解 dubbo 的一些基本東西躺坟,那么問個(gè)稍微難一點(diǎn)點(diǎn)的問題沦补,就是 spi,先問問你 spi 是啥咪橙?然后問問你 dubbo 的 spi 是怎么實(shí)現(xiàn)的夕膀?
其實(shí)就是看看你對(duì) dubbo 的掌握如何虚倒。
面試題剖析
spi 是啥?
spi产舞,簡(jiǎn)單來說魂奥,就是 service provider interface
,說白了是什么意思呢易猫,比如你有個(gè)接口耻煤,現(xiàn)在這個(gè)接口有 3 個(gè)實(shí)現(xiàn)類,那么在系統(tǒng)運(yùn)行的時(shí)候?qū)@個(gè)接口到底選擇哪個(gè)實(shí)現(xiàn)類呢准颓?這就需要 spi 了哈蝇,需要根據(jù)指定的配置或者是默認(rèn)的配置,去找到對(duì)應(yīng)的實(shí)現(xiàn)類加載進(jìn)來攘已,然后用這個(gè)實(shí)現(xiàn)類的實(shí)例對(duì)象炮赦。
舉個(gè)栗子。
你有一個(gè)接口A样勃。A1/A2/A3 分別是接口A的不同實(shí)現(xiàn)眼五。你通過配置 接口A=實(shí)現(xiàn)A2
,那么在系統(tǒng)實(shí)際運(yùn)行的時(shí)候彤灶,會(huì)加載你的配置看幼,用實(shí)現(xiàn)A2實(shí)例化一個(gè)對(duì)象來提供服務(wù)。
spi 機(jī)制一般用在哪兒幌陕?插件擴(kuò)展的場(chǎng)景诵姜,比如說你開發(fā)了一個(gè)給別人使用的開源框架,如果你想讓別人自己寫個(gè)插件搏熄,插到你的開源框架里面棚唆,從而擴(kuò)展某個(gè)功能,這個(gè)時(shí)候 spi 思想就用上了心例。
Java spi 思想的體現(xiàn)
spi 經(jīng)典的思想體現(xiàn)宵凌,大家平時(shí)都在用,比如說 jdbc止后。
Java 定義了一套 jdbc 的接口瞎惫,但是 Java 并沒有提供 jdbc 的實(shí)現(xiàn)類。
但是實(shí)際上項(xiàng)目跑的時(shí)候译株,要使用 jdbc 接口的哪些實(shí)現(xiàn)類呢瓜喇?一般來說,我們要根據(jù)自己使用的數(shù)據(jù)庫歉糜,比如 mysql乘寒,你就將 mysql-jdbc-connector.jar
引入進(jìn)來;oracle匪补,你就將 oracle-jdbc-connector.jar
引入進(jìn)來伞辛。
在系統(tǒng)跑的時(shí)候烂翰,碰到你使用 jdbc 的接口,他會(huì)在底層使用你引入的那個(gè) jar 中提供的實(shí)現(xiàn)類蚤氏。
dubbo 的 spi 思想
dubbo 也用了 spi 思想刽酱,不過沒有用 jdk 的 spi 機(jī)制,是自己實(shí)現(xiàn)的一套 spi 機(jī)制瞧捌。
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Protocol 接口棵里,在系統(tǒng)運(yùn)行的時(shí)候,姐呐,dubbo 會(huì)判斷一下應(yīng)該選用這個(gè) Protocol 接口的哪個(gè)實(shí)現(xiàn)類來實(shí)例化對(duì)象來使用殿怜。
它會(huì)去找一個(gè)你配置的 Protocol,將你配置的 Protocol 實(shí)現(xiàn)類曙砂,加載到 jvm 中來头谜,然后實(shí)例化對(duì)象,就用你的那個(gè) Protocol 實(shí)現(xiàn)類就可以了
上面那行代碼就是 dubbo 里大量使用的鸠澈,就是對(duì)很多組件柱告,都是保留一個(gè)接口和多個(gè)實(shí)現(xiàn),然后在系統(tǒng)運(yùn)行的時(shí)候動(dòng)態(tài)根據(jù)配置去找到對(duì)應(yīng)的實(shí)現(xiàn)類笑陈。如果你沒配置际度,那就走默認(rèn)的實(shí)現(xiàn)好了,沒問題涵妥。
@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 機(jī)制默認(rèn)是怎么玩兒的了蓬网,其實(shí)就是 Protocol 接口窒所,@SPI(“dubbo”)
說的是,通過 SPI 機(jī)制來提供實(shí)現(xiàn)類帆锋,實(shí)現(xiàn)類是通過 dubbo 作為默認(rèn) key 去配置文件里找到的吵取,配置文件名稱與接口全限定名一樣的,通過 dubbo 作為 key 可以找到默認(rèn)的實(shí)現(xiàn)類就是 com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
锯厢。
如果想要?jiǎng)討B(tài)替換掉默認(rèn)的實(shí)現(xiàn)類皮官,需要使用 @Adaptive
接口,Protocol 接口中哲鸳,有兩個(gè)方法加了 @Adaptive
注解臣疑,就是說那倆接口會(huì)被代理實(shí)現(xiàn)。
啥意思呢徙菠?
比如這個(gè) Protocol 接口搞了倆 @Adaptive
注解標(biāo)注了方法,在運(yùn)行的時(shí)候會(huì)針對(duì) Protocol 生成代理類郁岩,這個(gè)代理類的那倆方法里面會(huì)有代理代碼婿奔,代理代碼會(huì)在運(yùn)行的時(shí)候動(dòng)態(tài)根據(jù) url 中的 protocol 來獲取那個(gè) key缺狠,默認(rèn)是 dubbo,你也可以自己指定萍摊,你如果指定了別的 key挤茄,那么就會(huì)獲取別的實(shí)現(xiàn)類的實(shí)例了。
如何自己擴(kuò)展 dubbo 中的組件
下面來說說怎么來自己擴(kuò)展 dubbo 中的組件冰木。
自己寫個(gè)工程穷劈,要是那種可以打成 jar 包的,里面的 src/main/resources
目錄下踊沸,搞一個(gè) META-INF/services
歇终,里面放個(gè)文件叫:com.alibaba.dubbo.rpc.Protocol
,文件里搞一個(gè)my=com.bingo.MyProtocol
逼龟。自己把 jar 弄到 nexus 私服里去评凝。
然后自己搞一個(gè) dubbo provider
工程,在這個(gè)工程里面依賴你自己搞的那個(gè) jar腺律,然后在 spring 配置文件里給個(gè)配置:
<dubbo:protocol name=”my” port=”20000” />
provider 啟動(dòng)的時(shí)候奕短,就會(huì)加載到我們 jar 包里的my=com.bingo.MyProtocol
這行配置里,接著會(huì)根據(jù)你的配置使用你定義好的 MyProtocol 了匀钧,這個(gè)就是簡(jiǎn)單說明一下翎碑,你通過上述方式,可以替換掉大量的 dubbo 內(nèi)部的組件之斯,就是扔個(gè)你自己的 jar 包杈女,然后配置一下即可。
dubbo 里面提供了大量的類似上面的擴(kuò)展點(diǎn)吊圾,就是說达椰,你如果要擴(kuò)展一個(gè)東西,只要自己寫個(gè) jar项乒,讓你的 consumer 或者是 provider 工程啰劲,依賴你的那個(gè) jar,在你的 jar 里指定目錄下配置好接口名稱對(duì)應(yīng)的文件檀何,里面通過 key=實(shí)現(xiàn)類
蝇裤。
然后對(duì)對(duì)應(yīng)的組件,用類似 <dubbo:protocol>
用你的那個(gè) key 對(duì)應(yīng)的實(shí)現(xiàn)類來實(shí)現(xiàn)某個(gè)接口频鉴,你可以自己去擴(kuò)展 dubbo 的各種功能栓辜,提供你自己的實(shí)現(xiàn)。