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