32、SPI是啥思想?dubbo的SPI機制是怎麼玩兒的?
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原理.png