將ServiceLoader遷移到Java 9模塊系統 - frankel

19-10-15 banq
         

Service Loader允許在不同的JAR中分離API及其實現。客戶端代碼僅取決于API,而在運行時,將使用類路徑上的實現。這是將客戶端代碼與實現代碼分離的好方法。

為了說明這一點,讓我們實現自己的日志記錄項目:

public interface LogService {
? ? void log(String message);
}

public class LogStdOut implements LogService {
? ? @Override
? ? public void log(String message) {
? ? ? ? System.out.println(message);
? ? }
}

調用API并利用服務加載程序機制的客戶端:

public class Client {

? ? public static void main(String[] args) {
? ? ? ? ServiceLoader<LogService> loader = ServiceLoader.load(LogService.class);
? ? ? ? for (LogService service : loader) {
? ? ? ? ? ? service.log("Log written by " + service.getClass());
? ? ? ? }
? ? }
}

之所以如此神奇,是因為客戶端包含一個滿足一些約束的服務加載器配置文件:

  1. 它位于?/META-INF/services
  2. 它的名稱是接口的標準名稱
  3. 它包含實現類的標準類名:/META-INF/services/ch.frankel.blog.serviceloader.log.LogServicech.frankel.blog.serviceloader.log.stdout.LogStdOut

遷移到Java平臺模塊系統

關于我們的示例項目,需要執行以下步驟。

1.模塊化API:?為了使其他模塊(實現和客戶端)使用API??,LogService需要導出包含接口的包。

module-info.java

module log.api {
? ? exports ch.frankel.blog.serviceloader.log;
}

2.模塊化客戶端: 客戶端位于模塊依賴關系樹的邊界。它只需要API模塊。

module-info.java

module log.client {
? ? requires log.api;
}

3.?模塊化實施:?實現需要API。它還應該導出包含實現的包,以便可以在其他模塊中使用。但是,這還不夠。Java 9取代了Service Loader的工作方式,從META-INF/services文件夾到特定于模塊的實現。

為此,module-info語法提供了兩個關鍵字:使用provides引用接口并使用with指定實現:

module-info.java

import ch.frankel.blog.serviceloader.log.LogService;
import ch.frankel.blog.serviceloader.log.stdout.LogStdOut;

module log.stdout {
? ? requires log.api;
? ? exports ch.frankel.blog.serviceloader.log.stdout;
? ? provides LogService
? ? ? ? with LogStdOut;
}

有趣的是,僅配置發生了變化:客戶端中的Service Loader代碼本身沒有變化。

?

         

一级黄色录像影片 夫妻性生活影片 免费在线观看 一级a做爰片