Java SPI 將服務契約與一個或多個提供者,以及選取提供者的使用者分離。使用者依賴服務型別並使用ServiceLoader,因此不必讓應用程式依賴具體類別,即可在部署時新增提供者。
類別路徑服務 #
在模組出現之前,提供者 JAR 會在
META-INF/services/<fully.qualified.ServiceName>中列出實作。UTF-8 檔案每行包含一個提供者類別名稱;允許使用空白行和#註解。每個提供者都必須能被用於探索的載入器存取,並提供 public 無參數建構函式。
ServiceLoader<Codec> codecs = ServiceLoader.load(Codec.class);
for (Codec codec : codecs) {
if (codec.supports(mediaType)) return codec;
}探索與提供者具現化都是延遲執行的。反覆走訪提供者、以串流方式處理提供者,或呼叫Provider.get(),都可能載入並初始化提供者。因此,格式錯誤的設定、遺失的相依性、無法存取的型別或建構函式失敗,會以ServiceConfigurationError的形式浮現。應將提供者程式碼視為部署時提供的程式碼,在容器或外掛程式系統中選擇明確的類別載入器,並在受控的啟動邊界以診斷資訊失敗。
模組服務 #
使用 JPMS 時,使用者模組宣告uses com.example.Codec;提供者宣告provides com.example.Codec with com.example.JsonCodec。模組解析會依照模組系統規則考慮服務提供者。提供者類別不必僅為了ServiceLoader而匯出;具名模組中的提供者可以具有 public 無參數建構函式或 public static 提供者方法,但必須遵守服務宣告的限制。ServiceLoader.load(service, loader)遵循所提供載入器的可見性,而模組感知的多載則從模組層運作。載入器與層是服務邊界的一部分,而不是無關緊要的細節。
類別路徑設定仍受 unnamed module 和 automatic module 支援,因此可以進行遷移。不要在未測試已解析的提供者集合時同時發布兩種機制,因為重複項目與對順序的假設會使選擇結果無法移植。
JDBC 作為演進範例 #
早期使用 JDBC 驅動程式時,通常會呼叫Class.forName("vendor.Driver"),以觸發驅動程式向DriverManager進行 static 註冊。JDBC 4 引入了針對java.sql.Driver的服務提供者載入機制,使相容的驅動程式 JAR 能從META-INF/services/java.sql.Driver中被探索。現代應用程式仍應明確設定預期的驅動程式與連線屬性;自動探索移除的是啟動呼叫,而不是資料庫驗證或版本相容性方面的考量。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)