SpringBoot詳細(xì)介紹SPI機(jī)制示例
簡介
SPI(Service Provider Interface)是JDK內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制,可以用來啟用框架擴(kuò)展和替換組件,主要用于框架中開發(fā),例如Dubbo、Spring、Common-Logging,JDBC等采用采用SPI機(jī)制,針對同一接口采用不同的實(shí)現(xiàn)提供給不同的用戶,從而提高了框架的擴(kuò)展性。
Java SPI實(shí)現(xiàn)
Java內(nèi)置的SPI通過java.util.ServiceLoader類解析classPath和jar包的META-INF/services/目錄 下的以接口全限定名命名的文件,并加載該文件中指定的接口實(shí)現(xiàn)類,以此完成調(diào)用。
示例說明
創(chuàng)建動(dòng)態(tài)接口
public interface VedioSPI { void call(); }
實(shí)現(xiàn)類1
public class Mp3Vedio implements VedioSPI { @Override public void call() { System.out.println("this is mp3 call"); } }
實(shí)現(xiàn)類2
public class Mp4Vedio implements VedioSPI { @Override public void call() { System.out.println("this is mp4 call"); } }
在項(xiàng)目的source目錄下新建META-INF/services/目錄下,創(chuàng)建com.skywares.fw.juc.spi.VedioSPI文件。
相關(guān)測試
public class VedioSPITest { public static void main(String[] args) { ServiceLoader<VedioSPI> serviceLoader =ServiceLoader.load(VedioSPI.class); serviceLoader.forEach(t->{ t.call(); }); } }
說明:Java實(shí)現(xiàn)spi是通過ServiceLoader來查找服務(wù)提供的工具類。
運(yùn)行結(jié)果
源碼分析
上述只是通過簡單的示例來實(shí)現(xiàn)下java的內(nèi)置的SPI功能。其實(shí)現(xiàn)原理是ServiceLoader是Java內(nèi)置的用于查找服務(wù)提供接口的工具類,通過調(diào)用load()方法實(shí)現(xiàn)對服務(wù)提供接口的查找,最后遍歷來逐個(gè)訪問服務(wù)提供接口的實(shí)現(xiàn)類。
從源碼可以發(fā)現(xiàn):
- ServiceLoader類本身實(shí)現(xiàn)了Iterable接口并實(shí)現(xiàn)了其中的iterator方法,iterator方法的實(shí)現(xiàn)中調(diào)用了LazyIterator這個(gè)內(nèi)部類中的方法,解析完服務(wù)提供接口文件后最終結(jié)果放在了Iterator中返回,并不支持服務(wù)提供接口實(shí)現(xiàn)類的直接訪問。
- 所有服務(wù)提供接口的對應(yīng)文件都是放置在META-INF/services/目錄下,final類型決定了PREFIX目錄不可變更。
雖然java提供的SPI機(jī)制的思想非常好,但是也存在相應(yīng)的弊端。具體如下:
- Java內(nèi)置的方法方式只能通過遍歷來獲取
- 服務(wù)提供接口必須放到META-INF/services/目錄下。
針對java的spi存在的問題,Spring的SPI機(jī)制沿用的SPI的思想,但對其進(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示例
定義接口
public interface DataBaseSPI { void getConnection(); }
相關(guān)實(shí)現(xiàn)
#DB2實(shí)現(xiàn) public class DB2DataBase implements DataBaseSPI { @Override public void getConnection() { System.out.println("this database is db2"); } } #Mysql實(shí)現(xiàn) public class MysqlDataBase implements DataBaseSPI { @Override public void getConnection() { System.out.println("this is mysql database"); } }
1.在項(xiàng)目的META-INF目錄下,新增spring.factories文件
2.填寫相關(guān)的接口信息,內(nèi)容如下
com.skywares.fw.juc.springspi.DataBaseSPI = com.skywares.fw.juc.springspi.DB2DataBase, com.skywares.fw.juc.springspi.MysqlDataBase
說明多個(gè)實(shí)現(xiàn)采用逗號分隔。
相關(guān)測試類
public class SpringSPITest { public static void main(String[] args) { List<DataBaseSPI> dataBaseSPIs =SpringFactoriesLoader.loadFactories(DataBaseSPI.class, Thread.currentThread().getContextClassLoader()); for(DataBaseSPI datBaseSPI:dataBaseSPIs){ datBaseSPI.getConnection(); } } }
輸出結(jié)果
從示例中我們看出,Spring 采用spring.factories實(shí)現(xiàn)SPI與java實(shí)現(xiàn)SPI非常相似,但是spring的spi方式針對java的spi進(jìn)行的相關(guān)優(yōu)化具體內(nèi)容如下:
- Java SPI是一個(gè)服務(wù)提供接口對應(yīng)一個(gè)配置文件,配置文件中存放當(dāng)前接口的所有實(shí)現(xiàn)類,多個(gè)服務(wù)提供接口對應(yīng)多個(gè)配置文件,所有配置都在services目錄下;
- Spring factories SPI是一個(gè)spring.factories配置文件存放多個(gè)接口及對應(yīng)的實(shí)現(xiàn)類,以接口全限定名作為key,實(shí)現(xiàn)類作為value來配置,多個(gè)實(shí)現(xiàn)類用逗號隔開,僅spring.factories一個(gè)配置文件。
那么spring是如何通過加載spring.factories來實(shí)現(xiàn)SpI的呢?我們可以通過源碼來進(jìn)一步分析。
源碼分析
說明:loadFactoryNames解析spring.factories文件中指定接口的實(shí)現(xiàn)類的全限定名,具體實(shí)現(xiàn)如下:
說明: 獲取所有jar包中META-INF/spring.factories文件路徑,以枚舉值返回。 遍歷spring.factories文件路徑,逐個(gè)加載解析,整合factoryClass類型的實(shí)現(xiàn)類名稱,獲取到實(shí)現(xiàn)類的全類名稱后進(jìn)行類的實(shí)例話操作,其相關(guān)源碼如下:
說明:實(shí)例化是通過反射來實(shí)現(xiàn)對應(yīng)的初始化。
總結(jié)
本文詳細(xì)的講解了java和Spring的SPI機(jī)制,SPI技術(shù)將服務(wù)接口與服務(wù)實(shí)現(xiàn)進(jìn)行分離實(shí)現(xiàn)解耦,從而提升程序的可擴(kuò)展性。如有疑問,請隨時(shí)反饋。
到此這篇關(guān)于SpringBoot詳細(xì)介紹SPI機(jī)制示例的文章就介紹到這了,更多相關(guān)SpringBoot SPI機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文教你學(xué)會(huì)搭建SpringBoot分布式項(xiàng)目
這篇文章主要為大家詳細(xì)介紹了搭建SpringBoot分布式項(xiàng)目的相關(guān)知識,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01SpringBoot實(shí)現(xiàn)全局異常的封裝和統(tǒng)一處理
在Spring Boot應(yīng)用中,全局異常的處理是一個(gè)非常重要的方面,本文主要為大家詳細(xì)介紹了如何在Spring Boot中進(jìn)行全局異常的封裝和統(tǒng)一處理,需要的可以參考下2023-12-12springboot整合JSR303參數(shù)校驗(yàn)與全局異常處理的方法
JSR-303 是 JAVA EE 6 中的一項(xiàng)子規(guī)范,叫做 Bean Validation,官方參考實(shí)現(xiàn)是Hibernate Validator,這篇文章主要介紹了springboot整合JSR303參數(shù)校驗(yàn)與全局異常處理,需要的朋友可以參考下2022-09-09Hibernate 與 Mybatis 的共存問題,打破你的認(rèn)知!(兩個(gè)ORM框架)
這篇文章主要介紹了Hibernate 與 Mybatis 如何共存?本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08SpringCloud feign微服務(wù)調(diào)用之間的異常處理方式
這篇文章主要介紹了SpringCloud feign微服務(wù)調(diào)用之間的異常處理方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06基于Java Socket實(shí)現(xiàn)一個(gè)簡易在線聊天功能(一)
這篇文章主要給大家介紹基于Java Socket實(shí)現(xiàn)一個(gè)簡易在線聊天功能(一),分為客戶端和服務(wù)端兩段代碼,非常具有參考價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-05-05