首頁 >Java >java教程 >SpringBoot的SPI機制怎麼實現

SpringBoot的SPI機制怎麼實現

WBOY
WBOY轉載
2023-05-16 09:10:11966瀏覽

    簡介

    SPI(Service Provider Interface)是JDK內建的服務提供發現機制,可以用來啟用框架擴充和取代元件,主要用於框架中開發,例如Dubbo、Spring、Common-Logging,JDBC等採用SPI機制,針對同一介面採用不同的實作提供給不同的用戶,從而提高了框架的擴展性。

    Java SPI實作

    Java內建的SPI透過java.util.ServiceLoader類別解析classPath和jar套件的META-INF/services/目錄下的以介面全限定名命名的文件,並載入該文件中指定的介面實作類,以此完成呼叫。

    範例說明

    建立動態介面

    public interface VedioSPI
    {
        void call();
    }

    實作類別1

    public class Mp3Vedio implements VedioSPI
    {
        @Override
        public void call()
        {
            System.out.println("this is mp3 call");
        }
    }

    實作類別2

    public class Mp4Vedio implements VedioSPI
    {
        @Override
        public void call()
        {
           System.out.println("this is mp4 call");
        }
    }

    在專案的source目錄下新建META-INF/services/目錄下,建立com.skywares.fw.juc.spi.VedioSPI檔。

    SpringBoot的SPI機制怎麼實現

    相關測試

    public class VedioSPITest
    {
        public static void main(String[] args)
        {
            ServiceLoader<VedioSPI> serviceLoader =ServiceLoader.load(VedioSPI.class);
            serviceLoader.forEach(t->{
                t.call();
            });
        }
    }

    說明:Java實作spi是透過ServiceLoader來尋找服務提供的工具類別。

    運行結果

    SpringBoot的SPI機制怎麼實現

    源碼分析

    #上述只是透過簡單的範例來實現下java的內建的SPI功能。其實作原理是ServiceLoader是Java內建的用於查找服務提供介面的工具類,透過呼叫load()方法實作對服務提供介面的查找,最後遍歷來逐個存取服務提供介面的實作類別。

    SpringBoot的SPI機制怎麼實現

    從原始碼可以發現:

    • ServiceLoader類別本身實作了Iterable介面並實作了其中的iterator方法,iterator方法的實作中呼叫了LazyIterator這個內部類別中的方法,解析完服務提供介面檔案後最終結果放在了Iterator中返回,並不支援服務提供介面實作類別的直接存取。

    • 所有服務提供介面的對應檔案都是放置在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範例

    定義介面

    public interface DataBaseSPI
    {
       void getConnection();
    }

    相關實作

    #DB2实现
    public class DB2DataBase implements DataBaseSPI
    {
        @Override
        public void getConnection()
        {
            System.out.println("this database is db2");
        }
    }
    #Mysql实现
    public class MysqlDataBase implements DataBaseSPI
    {
        @Override
        public void getConnection()
        {
           System.out.println("this is mysql database");
        }
    }

    1.在專案的META-INF目錄下,新增spring.factories檔案

    SpringBoot的SPI機制怎麼實現

    2.填寫相關的介面信息,內容如下

    com.skywares.fw.juc.springspi.DataBaseSPI = com.skywares.fw .juc.springspi.DB2DataBase, com.skywares.fw.juc.springspi.MysqlDataBase

    說明多個實作採用逗號分隔。

    相關測試類別

    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();
             }
        }
    }

    輸出結果

    SpringBoot的SPI機制怎麼實現

    #從範例中我們看出,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的呢?我們可以透過原始碼來進一步分析。

    原始碼分析

    SpringBoot的SPI機制怎麼實現

    #:loadFactoryNames解析spring.factories檔案中指定介面的實作類別的全限定名,具體實作如下:

    SpringBoot的SPI機制怎麼實現

    說明: 取得所有jar套件中META-INF/spring.factories檔案路徑,以列舉值返回。遍歷spring.factories檔案路徑,逐一載入解析,整合factoryClass型別的實作類別名稱,取得到實作類別的全類別名稱後進行類別的實例話操作,其相關原始碼如下:

    SpringBoot的SPI機制怎麼實現

    #說明:實例化是透過反射來實現對應的初始化。

    以上是SpringBoot的SPI機制怎麼實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除