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

請(qǐng)問(wèn)板子的延遲函數(shù)是一種什么機(jī)制?
怎樣去設(shè)計(jì)一種同步補(bǔ)償機(jī)制(CDCO算法)?
是否有一種機(jī)制可以從虛擬功能處理程序進(jìn)行屏幕轉(zhuǎn)換呢?
一種基于最優(yōu)網(wǎng)格的動(dòng)態(tài)數(shù)據(jù)過(guò)濾機(jī)制
一種高效的磁盤(pán)隊(duì)列I/O機(jī)制
一種基于市場(chǎng)機(jī)制的隨機(jī)服務(wù)系統(tǒng)模型
一種ad hoc網(wǎng)信任聲譽(yù)評(píng)估機(jī)制
SPI模式下MMC卡的讀寫(xiě)機(jī)制

一種通用SPI接口的FPGA設(shè)計(jì)與實(shí)現(xiàn)

一種改進(jìn)的區(qū)塊鏈共識(shí)機(jī)制的研究與實(shí)現(xiàn)

可插拔組件設(shè)計(jì)機(jī)制—SPI介紹
什么是SPI機(jī)制

評(píng)論