一分鐘 瞭解 Java SPI 機制

什麼是 Java SPI

“Java SPI(Service Provider Interface),是JDK內置的一種動態加載擴展點的實現,是一種服務機制,SPI 的本質是將接口實現類的全限定名配置在文件中,並由服務加載機器讀取配置文件,加載實現類,這樣就是可以在運行時動態的爲接口替換實現類” ------ dubbo 官網

代碼實現

首先在ClassPath的META-INF/services目錄下放置一個與接口同名的文本文件,文件的內容爲接口的實現類,多個實現類用換行符分隔。JDK中使用java.util.ServiceLoader來加載具體的實現。

1.首先定義一個RobotCar接口

/**
 * @Auther: corey
 * @Date: 2020/7/3 10:05
 * @Description: qq飛車
 */
public interface RobotCar {

    void hello();
}

2.提供RobotCar的實現, RobotCar有兩個實現。BigPenguin和OldGodmother。

/**
 * @Auther: corey
 * @Date: 2020/7/3 10:06
 * @Description: 大企鵝
 */
public class BigPenguin implements RobotCar {

    @Override
    public void hello() {
        System.out.println("Hello  我是大企鵝。。。");
    }
}
/**
 * @Auther: corey
 * @Date: 2020/7/3 10:08
 * @Description: 老乾媽
 */
public class OldGodmother implements RobotCar {
    @Override
    public void hello() {
        System.out.println("hello 我是老幹ma。。。");
    }
}

3.添加配置文件 在META-INF/services目錄添加一個文件,文件名和接口全名稱相同,所以文件是META-INF/services/com.service.RobotCar文件內容爲:

service.impl.BigPenguin
service.impl.OldGodmother

4.通過ServiceLoader加載RobotCar實現

/**
 * @Auther: corey
 * @Date: 2020/7/3 10:17
 * @Description:
 */
public class RobotCarTest {

    public static void main(String[] args) {
        ServiceLoader<RobotCar> serviceLoader = ServiceLoader.load(RobotCar.class);
        System.out.println("Java SPI....");
        serviceLoader.forEach(RobotCar::hello);
    }
}

運行結果爲:
在這裏插入圖片描述
在上面的例子中,我們定義了一個擴展點和它的兩個實現。在ClassPath中添加了擴展的配置文件,最後使用ServiceLoader來加載所有的擴展點。 最終的輸出結

總結:

如圖所示:

在這裏插入圖片描述
Java SPI 實際上是“基於接口的編程+策略模式+配置文件”組合實現的動態加載機制。系統設計的各個抽象,往往有很多不同的實現方案,在面向的對象的設計裏,一般推薦模塊之間基於接口編程,模塊之間不對實現類進行硬編碼。一旦代碼裏涉及具體的實現類,就違反了可拔插的原則,如果需要替換一種實現,就需要修改代碼。爲了實現在模塊裝配的時候能不在程序裏動態指明,這就需要一種服務發現機制。

Java SPI就是提供這樣的一個機制:爲某個接口尋找服務實現的機制。有點類似IOC的思想,就是將裝配的控制權移到程序之外,在模塊化設計中這個機制尤其重要。所以SPI的核心思想就是解耦。

優點:
使用Java SPI機制的優勢是實現解耦,使得第三方服務模塊的裝配控制的邏輯與調用者的業務代碼分離,而不是耦合在一起。應用程序可以根據實際業務情況啓用框架擴展或替換框架組件。

缺點:

  • 雖然ServiceLoader也算是使用的延遲加載,但是基本只能通過遍歷全部獲取,也就是接口的實現類全部加載並實例化一遍。如果你並不想用某些實現類,它也被加載並實例化了,這就造成了浪費。獲取某個實現類,的方式不夠靈活

  • 多個併發多線程使用ServiceLoader類的實例是不安全的。

使用場景常見地方有

  • 數據庫驅動加載接口實現類的加載
    JDBC加載不同類型數據庫的驅動

  • 日誌門面接口實現類加載,SLF4J加載不同提供商的日誌實現類

  • Spring中大量使用了SPI,比如:對servlet3.0規範對ServletContainerInitializer的實現、自動類型轉換Type Conversion SPI(Converter SPI、Formatter SPI)等

  • Dubbo中也大量使用SPI的方式實現框架的擴展, 不過它對Java提供的原生SPI做了封裝,允許用戶擴展實現Filter接口

參考:
http://dubbo.apache.org/zh-cn/blog/introduction-to-dubbo-spi.html
https://developer.aliyun.com/article/640161

相關連接:
https://mp.weixin.qq.com/s/M0YIYOi9ucgnai1ILUVAvQ

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章