溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)

發(fā)布時(shí)間:2022-08-23 10:43:54 來(lái)源:億速云 閱讀:111 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)”的相關(guān)知識(shí),小編通過實(shí)際案例向大家展示操作過程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)”文章能幫助大家解決問題。

    簡(jiǎn)介

    SPI(Service Provider Interface)是JDK內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制,可以用來(lái)啟用框架擴(kuò)展和替換組件,主要用于框架中開發(fā),例如Dubbo、Spring、Common-Logging,JDBC等采用采用SPI機(jī)制,針對(duì)同一接口采用不同的實(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文件。

    SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)

    相關(guān)測(cè)試

    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來(lái)查找服務(wù)提供的工具類。

    運(yùn)行結(jié)果

    SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)

    源碼分析

    上述只是通過簡(jiǎn)單的示例來(lái)實(shí)現(xiàn)下java的內(nèi)置的SPI功能。其實(shí)現(xiàn)原理是ServiceLoader是Java內(nèi)置的用于查找服務(wù)提供接口的工具類,通過調(diào)用load()方法實(shí)現(xiàn)對(duì)服務(wù)提供接口的查找,最后遍歷來(lái)逐個(gè)訪問服務(wù)提供接口的實(shí)現(xiàn)類。

    SpringBoot的SPI機(jī)制怎么實(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ù)提供接口的對(duì)應(yīng)文件都是放置在META-INF/services/目錄下,final類型決定了PREFIX目錄不可變更。

    雖然java提供的SPI機(jī)制的思想非常好,但是也存在相應(yīng)的弊端。具體如下:

    • Java內(nèi)置的方法方式只能通過遍歷來(lái)獲取

    • 服務(wù)提供接口必須放到META-INF/services/目錄下。

    針對(duì)java的spi存在的問題,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示例

    定義接口

    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文件

    SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)

    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)采用逗號(hào)分隔。

    相關(guān)測(cè)試類

    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é)果

    SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)

    從示例中我們看出,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)類,多個(gè)服務(wù)提供接口對(duì)應(yīng)多個(gè)配置文件,所有配置都在services目錄下;

    • Spring factories SPI是一個(gè)spring.factories配置文件存放多個(gè)接口及對(duì)應(yīng)的實(shí)現(xiàn)類,以接口全限定名作為key,實(shí)現(xiàn)類作為value來(lái)配置,多個(gè)實(shí)現(xiàn)類用逗號(hào)隔開,僅spring.factories一個(gè)配置文件。

    那么spring是如何通過加載spring.factories來(lái)實(shí)現(xiàn)SpI的呢?我們可以通過源碼來(lái)進(jìn)一步分析。

    源碼分析

    SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)

    說明:loadFactoryNames解析spring.factories文件中指定接口的實(shí)現(xiàn)類的全限定名,具體實(shí)現(xiàn)如下:

    SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)

    說明: 獲取所有jar包中META-INF/spring.factories文件路徑,以枚舉值返回。 遍歷spring.factories文件路徑,逐個(gè)加載解析,整合factoryClass類型的實(shí)現(xiàn)類名稱,獲取到實(shí)現(xiàn)類的全類名稱后進(jìn)行類的實(shí)例話操作,其相關(guān)源碼如下:

    SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)

    說明:實(shí)例化是通過反射來(lái)實(shí)現(xiàn)對(duì)應(yīng)的初始化。

    關(guān)于“SpringBoot的SPI機(jī)制怎么實(shí)現(xiàn)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI