Java 模組

最後更新: 2026-02-19 04:54:04 +0800 +0800
tags: Java | Jpms | Modules
原文以英文撰寫。您正在閱讀AI翻譯版本。

Java 平台模組系統(JPMS)讓相依性與套件封裝變得明確。具名模組在其原始碼根目錄中包含描述元 module-info.java。編譯後會在展開的模組目錄或模組化 JAR 中產生 module-info.class。

java
module com.example.app {
    requires com.example.codec;
    uses com.example.codec.Decoder;
}

module com.example.codec {
    exports com.example.codec.api;
    exports com.example.codec.internal to com.example.app;
    opens com.example.codec.model to com.example.json;
    provides com.example.codec.Decoder with com.example.codec.JsonDecoder;
}

編譯、封裝與啟動 #

傳統的多模組原始碼樹會在模組原始碼路徑下,為每個模組建立一個目錄。使用以下命令編譯:

sh
javac --module-source-path src -d out -m com.example.app,com.example.codec
jar --create --file app.jar --main-class com.example.app.Main -C out/com.example.app .
java --module-path libs:app.jar --module com.example.app

jar --main-class 會記錄可執行的主類別,但它是選用的,因為啟動時可以使用 java -m module/name.Main 指定主類別。jlink 會從模組的傳遞閉包建立執行環境映像;它接受模組路徑上的模組化 JAR 與 JMOD,而 JMOD 不是執行環境構件。

sh
jlink --module-path "$JAVA_HOME/jmods:libs" \
      --add-modules com.example.app --output image
image/bin/java -m com.example.app

可讀性與封裝 #

requires 會讓模組可讀。requires transitive 會將這項可讀性重新匯出給相依模組;requires static 在編譯時期是必要的,但在執行時期可選。exports 會授予所有讀取模組,或透過 to 授予具名清單,對套件中的公開型別進行編譯時期與一般執行時期存取。opens 會授予深層反射,但不授予一般型別存取;open module 會開放其中所有套件。與框架整合時,優先使用限定的 opens。

uses 與 provides ... with ... 會在模組描述元中宣告服務。模組解析期間,ServiceLoader 會使用這些宣告。套件不是獨立於模組之外的存取單位,因此只有公開修飾詞永遠無法繞過未匯出的套件。

解析與遷移 #

在每個階段,模組路徑加上系統模組與升級模組,構成可觀察模組的集合。解析會從根模組開始,沿著 requires 邊追蹤;重複的模組名稱會造成失敗,而不是允許類別路徑順序選擇其中一個。具名模組不能包含分割套件:同一套件出現在兩個已解析的具名模組中會產生錯誤。這項規則讓套件擁有權與封裝具有確定性。

類別路徑程式碼屬於未命名模組。它會讀取已解析具名模組的匯出套件,其套件會被匯出並開放,但具名模組不能宣告對未命名模組的 requires。模組路徑上的非模組化 JAR 會成為自動模組:它具有衍生名稱或資訊清單名稱,能讀取所有已解析模組,並匯出及開放所有套件。自動模組有助於分階段遷移,但不提供有意義的封裝;在依賴模組邊界之前,應以明確描述元取代它們。

跨越具名模組邊界進行反射,需要一般可存取性以及開放的套件以取得深層存取。--add-exports 與 --add-opens 是遷移或部署覆寫,不是由模組擁有的描述元之替代品。

參考資料 #

評論

在 GitHub 上閱讀或評論(發表評論需要登入)