SPI(Service Provider Interface)是JDK內置的一種服務提供發現機制,可以用來啟用框架擴展和替換組件,主要用于框架中開發,例如Dubbo、Spring、Common-Logging,JDBC等采用采用SPI機制,針對同一接口采用不同的實現提供給不同的用戶,從而提高了框架的擴展性。
Java SPI實現
Java內置的SPI通過java.util.ServiceLoader類解析classPath和jar包的META-INF/services/目錄 下的以接口全限定名命名的文件,并加載該文件中指定的接口實現類,以此完成調用。
示例說明
創建動態接口
publicinterfaceVedioSPI { voidcall(); }
實現類1
publicclassMp3VedioimplementsVedioSPI { @Override publicvoidcall() { System.out.println("thisismp3call"); } }
實現類2
publicclassMp4VedioimplementsVedioSPI { @Override publicvoidcall() { System.out.println("thisismp4call"); } }
在項目的source目錄下新建META-INF/services/目錄下,創建com.skywares.fw.juc.spi.VedioSPI文件。
相關測試
publicclassVedioSPITest { publicstaticvoidmain(String[]args) { ServiceLoaderserviceLoader=ServiceLoader.load(VedioSPI.class); serviceLoader.forEach(t->{ t.call(); }); } }
說明:Java實現spi是通過ServiceLoader來查找服務提供的工具類。
運行結果:
源碼分析
上述只是通過簡單的示例來實現下java的內置的SPI功能。其實現原理是ServiceLoader是Java內置的用于查找服務提供接口的工具類,通過調用load()方法實現對服務提供接口的查找,最后遍歷來逐個訪問服務提供接口的實現類。
從源碼可以發現:
ServiceLoader類本身實現了Iterable接口并實現了其中的iterator方法,iterator方法的實現中調用了LazyIterator這個內部類中的方法,迭代器創建實例。
所有服務提供接口的對應文件都是放置在META-INF/services/目錄下,final類型決定了PREFIX目錄不可變更。
雖然java提供的SPI機制的思想非常好,但是也存在相應的弊端。具體如下:
Java內置的方法方式只能通過遍歷來獲取
服務提供接口必須放到META-INF/services/目錄下。
針對java的spi存在的問題,Spring的SPI機制沿用的SPI的思想,但對其進行擴展和優化。
Spring SPI
Spring SPI沿用了Java SPI的設計思想,Spring采用的是spring.factories方式實現SPI機制,可以在不修改Spring源碼的前提下,提供Spring框架的擴展性。
Spring 示例
定義接口
publicinterfaceDataBaseSPI { voidgetConnection(); }
相關實現
##DB2實現 publicclassDB2DataBaseimplementsDataBaseSPI { @Override publicvoidgetConnection() { System.out.println("thisdatabaseisdb2"); } } ##Mysql實現 publicclassMysqlDataBaseimplementsDataBaseSPI { @Override publicvoidgetConnection() { System.out.println("thisismysqldatabase"); } }
1、在項目的META-INF目錄下,新增spring.factories文件
2、填寫相關的接口信息,內容如下:
com.skywares.fw.juc.springspi.DataBaseSPI=com.skywares.fw.juc.springspi.DB2DataBase,com.skywares.fw.juc.springspi.MysqlDataBase
說明多個實現采用逗號分隔。
相關測試類
publicclassSpringSPITest { publicstaticvoidmain(String[]args) { ListdataBaseSPIs=SpringFactoriesLoader.loadFactories(DataBaseSPI.class, Thread.currentThread().getContextClassLoader()); for(DataBaseSPIdatBaseSPI:dataBaseSPIs){ datBaseSPI.getConnection(); } } }
輸出結果
從示例中我們看出,Spring 采用spring.factories實現SPI與java實現SPI非常相似,但是spring的spi方式針對java的spi進行的相關優化具體內容如下:
Java SPI是一個服務提供接口對應一個配置文件,配置文件中存放當前接口的所有實現類,多個服務提供接口對應多個配置文件,所有配置都在services目錄下;
Spring factories SPI是一個spring.factories配置文件存放多個接口及對應的實現類,以接口全限定名作為key,實現類作為value來配置,多個實現類用逗號隔開,僅spring.factories一個配置文件。
那么spring是如何通過加載spring.factories來實現SpI的呢?我們可以通過源碼來進一步分析。
源碼分析
說明:loadFactoryNames解析spring.factories文件中指定接口的實現類的全限定名,具體實現如下:
說明:獲取所有jar包中META-INF/spring.factories文件路徑,以枚舉值返回。遍歷spring.factories文件路徑,逐個加載解析,整合factoryClass類型的實現類名稱,獲取到實現類的全類名稱后進行類的實例話操作,其相關源碼如下:
說明:實例化是通過反射來實現對應的初始化。
審核編輯:劉清
-
JAVA
+關注
關注
19文章
2980瀏覽量
105673 -
SPI
+關注
關注
17文章
1731瀏覽量
92836 -
JDBC
+關注
關注
0文章
25瀏覽量
13462
原文標題:深入剖析 Spring Boot 的 SPI 機制
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
JDK動態代理的原理
Java的SPI機制詳解

請問板子的延遲函數是一種什么機制?
是否有一種機制可以從虛擬功能處理程序進行屏幕轉換呢?
一種基于最優網格的動態數據過濾機制
一種高效的磁盤隊列I/O機制
一種基于市場機制的隨機服務系統模型
一種ad hoc網信任聲譽評估機制
SPI模式下MMC卡的讀寫機制

一種通用SPI接口的FPGA設計與實現

一種改進的區塊鏈共識機制的研究與實現

評論