1. 程式人生 > >作為一個Java工程師,你應該要知道SPI機制

作為一個Java工程師,你應該要知道SPI機制

什麼是 SPI

SPI是Service Provider Interface的簡稱,是JDK預設提供的一種將介面和實現類進行分離的機制。這種機制能將介面和實現進行解耦,大大提升系統的可擴充套件性。

SPI機制約定:當一個Jar包需要提供一個介面的實現類時,這個Jar包需要在META-INF/services/目錄裡同時建立一個以服務介面命名的檔案。該檔案裡就是實現該服務介面的具體實現類。而當外部程式裝配這個模組的時候,就能通過該Jar包META-INF/services/裡的配置檔案找到具體的實現類名,並裝載例項化,完成模組的注入。

比如下面的列子,jcl-over-slf4j這個Jar包提供了conmon-logging中LogFactory

這個介面的實現。

檔案中的內容如下:

# 這裡表名具體的實現類是`org.apache.commons.logging.impl.SLF4JLogFactory`這個類
org.apache.commons.logging.impl.SLF4JLogFactory

# Axis gets at JCL through its own mechanism as defined by Commons Discovery, which
# in turn follows the instructions found at: 
# http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service Provider

JDK為了方便查詢服務的實現,還提供了一個工具類:java.util.ServiceLoader。

ServiceLoader<Object> loader = ServiceLoader.load(LogFactory);
loader.forEach((item)->{
    System.out.println(item);
});

上面程式碼中使用ServiceLoader遍歷使用SPI機制提供的所有LogFactory實現。

應用場景

SPI機制的主要應用有框架擴充套件和元件的替換等,比如

  • JDBC介面實現類的執行時載入:我們連線具體的資料庫是都需要新增相關的Jar包依賴,但是不需要我們再做任何其他配置,只要將Jar包放到classpath下就行了。這是一個最常見的SPI應用場景。
  • 日誌門面載入具體的日誌實現類:之前的部落格中介紹到,jcl和slf4j等只是日誌實現類,Log4j和LOgBack才是具體的日誌實現。JCL和SLF4J載入日誌實現類時也使用了SPI機制,具體請看上面章節中舉的列子。
  • Spring中大量使用了SPI:比如對servlet3.0規範對ServletContainerInitializer的實現、自動型別轉換Type Conversion SPI(Converter SPI、Formatter SPI)等

自己實現

下面就一步步從定義介面到提供SPI實現類來演示下SPI機制具體的使用方式。

step1:先定義一個介面

public interface SaySomething {

    String say(String name);

}

step2:編寫實現類

public class ASaySomething implements SaySomething {
    @Override
    public String say(String name) {
        return "Hi,"+name+", l am A...";
    }
}

step3:在resource下新增META-INFO/services目錄
新增完這個目錄後,新增一個以SaySomething介面的全限定名為名字的檔案,這個檔案的內容是你要設定的具體實現類。這邊我們就設定實現類為上面的ASaySomething

step4:使用SPI機制

public static void main(String[] args) {
    ServiceLoader<SaySomething> loader = ServiceLoader.load(SaySomething.class);
    loader.forEach(item ->{item.say("csx");});
}

API和SPI的比較

在開發中我們還經常會提到API這個名詞,下面也總結下兩者的區別:

  • API (Application Programming Interface)在大多數情況下,都是實現方制定介面並完成對介面的實現,呼叫方僅僅依賴介面呼叫,且無權選擇不同實現。 從使用人員上來說,API 直接被應用開發人員使用。

  • SPI (Service Provider Interface)是呼叫方來制定介面規範,提供給外部來實現,呼叫方在呼叫時則選擇自己需要的外部實現。 從使用人員上來說,SPI 被框架擴充套件人員使用。

優缺點

優點

  • 使用Java SPI機制的優勢是實現解耦,使得第三方服務模組的裝配控制的邏輯與呼叫者的業務程式碼分離,而不是耦合在一起。應用程式可以根據實際業務情況啟用框架擴充套件或替換框架元件

缺點

  • SPI必須先將介面的所有實現類都遍歷出來才能最後選擇具體使用哪個類。有些不要的類也會被例項化,可能會比較浪費記憶體。
  • ServiceLoader並不是執行緒安全的。

參考

  • https://www.jianshu.com/p/46b42f7f593c
  • https://www.cnblogs.com/jy107600/p/11464985.html