SPI 實現原理及運用

SPI原理

 

SPI的全名爲Service Provider Interface.大多數開發人員可能不熟悉,由於這個是針對廠商或者插件的。在java.util.ServiceLoader的文檔裏有比較詳細的介紹。簡單的總結下java spi機制的思想。咱們系統裏抽象的各個模塊,每每有不少不一樣的實現方案,好比日誌模塊的方案,xml解析模塊、jdbc模塊的方案等。面向對象的設計裏,咱們通常推薦模塊之間基於接口編程,模塊之間不使用實現類進行硬編碼。一旦代碼裏涉及具體的實現類,就違反了可拔插的原則,若是須要替換一種實現,就須要修改代碼。爲了實如今模塊裝配的時候動態指定具體實現類,這就須要一種服務發現機制。 java spi就是提供這種功能的機制:爲某個接口尋找服務實現的機制。有點相似IOC的思想,將裝配的控制權移到程序以外,在模塊化設計中這個機制尤爲重要。 java

java SPI應用場景很普遍,在Java底層和一些框架中都很經常使用,好比java數據驅動加載和Dubbo。Java底層定義加載接口後,由不一樣的廠商提供驅動加載的實現方式,當咱們須要加載不一樣的數據庫的時候,只須要替換數據庫對應的驅動加載jar包,就能夠進行使用。數據庫

 

要使用Java SPI,須要遵循以下約定:編程

  • 一、當服務提供者提供了接口的一種具體實現後,在jar包的META-INF/services目錄下建立一個以「接口全限定名」爲命名的文件,內容爲實現類的全限定名;api

  • 二、接口實現類所在的jar包放在主程序的classpath中;安全

  • 三、主程序經過java.util.ServiceLoder動態裝載實現模塊,它經過掃描META-INF/services目錄下的配置文件找到實現類的全限定名,把類加載到JVM;多線程

  • 四、SPI的實現類必須攜帶一個不帶參數的構造方法;併發

SPI實現

1. 創建maven工程

(1)spi-demo-api 提供須要實現的接口

public interface SpiDemo {
    void say();
}

(2)spi-demo-impl1 爲第一種實現

public class SpiDemoImpl1 implements SpiDemo {
    public void say() {
        System.out.println("SpiDemoImpl1");
    }
}

每個SPI接口都須要在本身項目的靜態資源目錄中聲明一個services文件,文件名爲實現規範接口的類名全路徑。在resources目錄中建立\META-INF\services目錄,建立以com.hanggle.spi.api.SpiDemo爲名的文件。(文件名便是要實現的接口類的全路徑以下圖)框架

 

文件內容:maven

com.hanggle.spi.api.impl1.SpiDemoImpl1

 

(3)spi-demo-impl2 爲第二種實現 

public class SpiDemoImpl2 implements SpiDemo {
    public void say() {
        System.out.println("SpiDemoImpl2");
    }
}

同spi-demo-impl1同樣ide

在resources目錄中建立\META-INF\services目錄,建立以com.hanggle.spi.api.SpiDemo爲名的文件。

 文件內容:

com.hanggle.spi.api.impl1.SpiDemoImpl2

 

(4)spi-demo-main 程序中的使用

public static void main(String[] args) {
        ServiceLoader<SpiDemo> serviceLoader = ServiceLoader.load(SpiDemo.class);
        for (SpiDemo o : serviceLoader) {
            o.say();
        }
    }

運行結果:

 

 SPI 源碼

裝配文件路徑的定義:

有源代碼能夠,java會根據定義的路徑去掃描可能存在的接口的實現。放在config中,而後使用parse方法將配置文件中的接口實現全路徑放在pending中,並取得第一個實現類(變量nextName),

而後使用類加載器加載,加載須要調用的類,而後調用實現的方法

 

優缺點

優勢:

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

 

缺點:

  • 雖然ServiceLoader也算是使用的延遲加載,可是基本只能經過遍歷所有獲取,也就是接口的實現類所有加載並實例化一遍。若是你並不想用某些實現類,它也被加載並實例化了,這就形成了浪費。獲取某個實現類的方式不夠靈活,只能經過Iterator形式獲取,不能根據某個參數來獲取對應的實現類。

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

 

 參考:http://www.pandan.xyz/2017/03/14/java%20SPI%E6%9C%BA%E5%88%B6%E5%8E%9F%E7%90%86/

https://yq.aliyun.com/articles/640161

相關文章
相關標籤/搜索