1. 程式人生 > 實用技巧 >Java SPI(服務發現機制)

Java SPI(服務發現機制)

SPI 全稱為Service Provider Interface,是一種服務發現機制。SPI 的本質是將介面實現類的全限定名配置在檔案中,並由服務載入器讀取配置檔案,載入實現類。

這樣可以在執行時,動態為介面替換實現類。正因此特性,我們可以很容易的通過 SPI 機制為我們的程式提供拓展功能。

簡單點說SPI就是 JDK 內建的一個服務發現機制,它使得介面和具體實現完全解耦。我們只宣告介面,具體的實現類在配置中選擇。

具體的就是你定義了一個介面,然後在META-INF/services目錄下放置一個與介面同名的文字檔案,檔案的內容為介面的實現類,多個實現類用換行符分隔。

這樣就通過配置來決定具體用哪個實現!

例如,使用 Java 語言訪問資料庫時我們會使用到 java.sql.Driver 介面,不同資料庫產品底層的協議不同,提供的 java.sql.Driver 實現也不同,在開發 java.sql.Driver 介面時,開發人員並不清楚使用者最終會使用哪個資料庫,在這種情況下就可以使用 Java SPI 機制在實際執行過程中,為 java.sql.Driver 介面尋找具體的實現。

當服務的提供者提供了一種介面的實現之後,需要在 Classpath 下的 META-INF/services/ 目錄裡建立一個以服務介面命名的檔案,此檔案記錄了該 jar 包提供的服務介面的具體實現類。當某個應用引入了該 jar 包且需要使用該服務時,JDK SPI 機制就可以通過查詢這個 jar 包的 META-INF/services/ 中的配置檔案來獲得具體的實現類名,進行實現類的載入和例項化,最終使用該實現類完成業務功能。

下面我們通過一個簡單的示例演示下 JDK SPI 的基本使用方式:

首先我們需要建立一個 Log 介面,來模擬日誌列印的功能:

public interface Log { 
    void log(String info); 
} 

接下來提供兩個實現—— Logback 和 Log4j,分別代表兩個不同日誌框架的實現,如下所示:

public class Logback implements Log { 
    @Override 
    public void log(String info) { 
        System.out.println(
"Logback:" + info); } } public class Log4j implements Log { @Override public void log(String info) { System.out.println("Log4j:" + info); } }

在專案的 resources/META-INF/services 目錄下新增一個名為 com.xxx.Log 的檔案,這是 JDK SPI 需要讀取的配置檔案,具體內容如下:

com.xxx.impl.Log4j 
com.xxx.impl.Logback 

最後建立 main() 方法,其中會載入上述配置檔案,建立全部 Log 介面實現的例項,並執行其 log() 方法,如下所示:

public class Main { 
    public static void main(String[] args) { 
        ServiceLoader<Log> serviceLoader =  
                ServiceLoader.load(Log.class); 
        Iterator<Log> iterator = serviceLoader.iterator(); 
        while (iterator.hasNext()) { 
            Log log = iterator.next(); 
            log.log("JDK SPI");  
        } 
    } 
} 
// 輸出如下: 
// Log4j:JDK SPI 
// Logback:JDK SPI