Java SPI機制詳細介紹
為什麼需要SPI?
思考一個場景,我們封裝瞭一套服務,別人通過引入我們寫好的包,就可以使用這些接口API,完成相應的操作,這本來沒有什麼問題,但是會存在使用該服務的實體有不相同的業務需求,需要進一步的擴展,但是由於api是寫好的,想要擴展並非那麼的簡單,如果存在這樣子的場景,我們該怎麼辦?
可以使用Java 提供的SPI機制
什麼是SPI?SPI和API的區別
SPI
SPI的全稱是Service Provider Interface
,是Java提供的可用於第三方實現和擴展的機制,通過該機制,我們可以實現解耦,SPI接口方負責定義和提供默認實現,SPI調用方可以按需擴展
API的全稱是Application Programming Interface
,廣義上來看就是接口,負責程序與程序之間進行協作的通道,就好比上面給的例子,【我們封裝好瞭一套服務,通過API的形式提供給他人使用,別人使用API就能得到想要的】
所以他們倆的區別就很明顯瞭,API的調用方隻能依賴使用提供方的實現,SPI就如同可定制化的API一樣,調用方可以自定義實現替換API提供的默認實現
來人,上點對抗
首先,我們新建一個空的maven項目,裡邊有兩個包
spi-provider從名字就可以得知是SPI的提供方
spi-user SPI的使用方
spi-provider
我們簡單定義一個SPI接口,就叫ISpiTest
,裡邊就一個saySomething
方法,再提供一個默認的實現
public interface ISpiTest { void saySomething(); } public class DefaultSpiImplementation implements ISpiTest{ @Override public void saySomething() { System.out.println("[默認實現] -> 今天也是充滿希望的一天"); } }
然後,模擬走流程,註意步驟4是我們之後要自定義替換的
/** * 模擬一套流程 * @author Amg * @date 2021/12/9 */ public class TestUtils { public static void workFlow(ISpiTest s) { System.out.println("1、步驟1......."); System.out.println("2、步驟2......."); System.out.println("3、步驟3......."); System.out.print("4、步驟4:"); s.saySomething(); System.out.println("5、步驟5......."); } }
接著,重點來瞭,我們需要在resources目錄下面創建/META-INF/services
文件夾,然後以SPI接口的全限定類名作為名稱創建一個文件
往文件裡面填寫實現類的全限定類名,如下
com.amg.spi.DefaultSpiImplementation
到此,spi-provider這個模塊就完成瞭,至於之後要怎麼使用,到spi-user模塊中進一步說明
spi-user
首先,我們在pom文件中,引入spi-provider
坐標依賴
然後定義main方法,在main方法中調用在spi-provider
中定義的SPI接口,此時采用的是默認的配置
可以註意到我們使用ServiceLoader
這個類的load
方法,傳入SPI接口的字節碼進行構造,我們在spi-provider中resources中給出瞭一個默認實現,但是我們是在spi-user中去調用的,ServiceLoader會自動讀取META-INF下的配置文件,就算是跨jar包也是可以的
然後現在我們在spi-user中定義一個實現類,以及把他配置到META-INF下(需要註意,這個配置的全限定類名仍然需要是spi-provider中定義SPI接口的路徑),來看看效果
spi-user下META-INF裡邊內容如下
com.amg.spiuser.service.impl.WantHamburger
可以發現,我們並沒有改變任何的客戶端代碼,隻是把配置文件進行瞭簡單的修改,即可完成自定義實現,這就是使用SPI的魅力
🤔思考一下,我們之前的流程是怎麼做的
首先定義瞭一個接口,面向接口編程嘛定義配置文件各個自定義的實現類,隻需要按照規則重寫配置文件即可
總結
通過這個流程,我們可以歸納為一句話,SPI是策略模式的一種體現,配合面向接口編程的思想以及必要的配置文件,即可完成定義和具體實現的解耦,而且是可定制化的API
SPI的優點有以下
定制化實現接口解耦
SPI的缺點有以下
通過觀察ServiceLoader,可以發現並沒有額外的加鎖機制,所以會存在並發問題獲取對應的實現類不夠靈活,從上面例子可以看出,需要使用迭代器的方式獲取需要知道接口的所有具體實現類,所以每次都要加載和實例化所有的實現類
實際中,SPI的使用還是很常見的,例如Dubbo和Spring Boot都為我們提供瞭一套SPI機制,隻不過此SPI是在Java提供的SPI機制基礎上進行改造而來,有興趣的同學也可以去查下資料,增長增長
到此這篇關於Java SPI機制詳細介紹的文章就介紹到這瞭,更多相關Java SPI機制內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!