javafx jlink 遇到的非模組化的依賴打包報錯“模組異常”的問題和處理

南怪布德發表於2024-09-20

簡介

javafx:jlink 是 javafx-maven-plugin 外掛中的一個目標,用於建立一個自包含的 JavaFX 應用程式執行時映像。這個目標利用 Java 的 jlink 工具來生成一個包含應用程式及其所有依賴的定製化執行時映像,從而簡化部署和分發。

  • 建立自包含執行時:透過 jlink 建立一個定製的 Java 執行時環境(JRE),只包含應用程式所需的模組和依賴。這可以減少最終部署包的大小。
  • 模組化:與傳統的 JRE 相比,生成的執行時映像是模組化的,只包含你的應用程式實際需要的 Java 模組。
  • 簡化部署:可以將生成的執行時映像作為終端使用者的執行環境,減少對系統環境的依賴。

打包遇到非模組依賴時的處理

一般 javafx:jilink 打包需要依賴的專案都是模組化的,然後使用外掛 javafx-maven-plugin進行 javafx:jlink 一鍵打包

<plugin>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-maven-plugin</artifactId>
    <version>0.0.8</version>
    <configuration>
        <stripDebug>true</stripDebug>
        <compress>2</compress>
        <noHeaderFiles>true</noHeaderFiles>
        <noManPages>true</noManPages>
        <launcher>FxExample</launcher>
        <jlinkImageName>FxExample</jlinkImageName>
        <jlinkZipName>FxExampleZip</jlinkZipName>
        <mainClass>com.xxx.xxx.FxExampleApp</mainClass>
    </configuration>
</plugin>

但是總會有那種沒有模組化的依賴,比如說 hutool 包,預設是沒有模組化的,這個時候就需要自行補全這個模組化的資訊
首先在倉庫中找到所有依賴的沒有模組化的包,我這裡用 hutool 舉例子
我的專案依賴了(其中 setting 依賴了 log 和 core)

hutool-core-5.8.29.jar
hutool-json-5.8.29.jar
hutool-setting-5.8.29.jar
hutool-log-5.8.29.jar

準備臨時編譯目錄

新建一個臨時的空的 maven 專案,或者一個空資料夾,我這裡為了方便,使用的空的 maven 專案
將依賴的 jar 複製到專案中,從控制檯 cd 到專案路徑

在專案路徑下面為每個 jar 生成 module-info.java

# 使用jdk自帶的工具掃描自動生成module-info.java
jdeps --ignore-missing-deps --generate-module-info . *.jar

日誌

Warning: --ignore-missing-deps specified. Missing dependencies from cn.hutool.setting are ignored
writing to .\cn.hutool.setting\module-info.java
writing to .\cn.hutool.json\module-info.java
Warning: --ignore-missing-deps specified. Missing dependencies from cn.hutool.core are ignored
writing to .\cn.hutool.core\module-info.java

生成的內容比如 cn.hutool.core/module-info.java
這裡建議直接將整個 module open 了,方便反射機制的呼叫,還有如果遇到需要 uses 的介面,也需要加到模組資訊中,不然後續打完包執行過程遇到對應的類,還是會報模組沒有配置某個類的 uses 資訊,以下是我根據我的情況修改了內容的 module-info

open module cn.hutool.core {
    requires java.management;
    requires transitive java.compiler;
    requires transitive java.datatransfer;
    requires transitive java.desktop;
    requires transitive java.naming;
    requires transitive java.sql;
    requires transitive java.xml;
    uses cn.hutool.core.convert.Converter;
    uses cn.hutool.core.convert.TypeConverter;
    exports cn.hutool.core.annotation.scanner;
    exports cn.hutool.core.annotation;
    exports cn.hutool.core.bean.copier.provider;
    exports cn.hutool.core.bean.copier;
    exports cn.hutool.core.bean;
    exports cn.hutool.core.builder;
    exports cn.hutool.core.clone;
    exports cn.hutool.core.codec;
    exports cn.hutool.core.collection;
    exports cn.hutool.core.comparator;
    exports cn.hutool.core.compiler;
    exports cn.hutool.core.compress;
    exports cn.hutool.core.convert.impl;
    exports cn.hutool.core.convert;
    exports cn.hutool.core.date.chinese;
    exports cn.hutool.core.date.format;
    exports cn.hutool.core.date;
    exports cn.hutool.core.exceptions;
    exports cn.hutool.core.getter;
    exports cn.hutool.core.img.gif;
    exports cn.hutool.core.img;
    exports cn.hutool.core.io.checksum.crc16;
    exports cn.hutool.core.io.checksum;
    exports cn.hutool.core.io.copy;
    exports cn.hutool.core.io.file.visitor;
    exports cn.hutool.core.io.file;
    exports cn.hutool.core.io.resource;
    exports cn.hutool.core.io.unit;
    exports cn.hutool.core.io.watch.watchers;
    exports cn.hutool.core.io.watch;
    exports cn.hutool.core.io;
    exports cn.hutool.core.lang.ansi;
    exports cn.hutool.core.lang.caller;
    exports cn.hutool.core.lang.copier;
    exports cn.hutool.core.lang.func;
    exports cn.hutool.core.lang.generator;
    exports cn.hutool.core.lang.hash;
    exports cn.hutool.core.lang.id;
    exports cn.hutool.core.lang.intern;
    exports cn.hutool.core.lang.loader;
    exports cn.hutool.core.lang.mutable;
    exports cn.hutool.core.lang.reflect;
    exports cn.hutool.core.lang.tree.parser;
    exports cn.hutool.core.lang.tree;
    exports cn.hutool.core.lang;
    exports cn.hutool.core.map.multi;
    exports cn.hutool.core.map;
    exports cn.hutool.core.math;
    exports cn.hutool.core.net.multipart;
    exports cn.hutool.core.net.url;
    exports cn.hutool.core.net;
    exports cn.hutool.core.stream;
    exports cn.hutool.core.swing.clipboard;
    exports cn.hutool.core.swing;
    exports cn.hutool.core.text.csv;
    exports cn.hutool.core.text.escape;
    exports cn.hutool.core.text.finder;
    exports cn.hutool.core.text.replacer;
    exports cn.hutool.core.text.split;
    exports cn.hutool.core.text;
    exports cn.hutool.core.thread.lock;
    exports cn.hutool.core.thread.threadlocal;
    exports cn.hutool.core.thread;
    exports cn.hutool.core.util;
}

編譯 module-info.java 為 module-info.class 並且注入 module-info.class 到 jar

這裡需要注意一下編譯的順序,對於 hutool-core 模組來說,它沒有其他的依賴模組,可以先直接編譯這個模組

# 生成module-info.class
javac --patch-module cn.hutool.core=hutool-core-5.8.29.jar cn.hutool.core/module-info.java
# 注入module-info.class到jar
jar uf hutool-core-5.8.29.jar -C cn.hutool.core module-info.class

其他模組編譯和注入(增加-p 來引入依賴的模組)

javac --patch-module cn.hutool.core=hutool-core-5.8.29.jar cn.hutool.core/module-info.java
jar uf hutool-core-5.8.29.jar -C cn.hutool.core module-info.class

javac -p hutool-core-5.8.29.jar --patch-module cn.hutool.json=hutool-json-5.8.29.jar cn.hutool.json/module-info.java
jar uf hutool-json-5.8.29.jar -C cn.hutool.json module-info.class

javac -p hutool-core-5.8.29.jar --patch-module cn.hutool.log=hutool-log-5.8.29.jar cn.hutool.log/module-info.java
jar uf hutool-log-5.8.29.jar -C cn.hutool.log module-info.class

javac -p "hutool-core-5.8.29.jar;hutool-log-5.8.29.jar" --patch-module cn.hutool.setting=hutool-setting-5.8.29.jar cn.hutool.setting/module-info.java
jar uf hutool-setting-5.8.29.jar -C cn.hutool.setting module-info.class

然後將處理好之後的 jars 複製到原專案 libs 目錄,修改 maven 依賴,如果 idea 報紅,則將 libs 新增到專案的 library

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-core</artifactId>
    <version>5.8.29</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/libs/hutool-core-5.8.29.jar</systemPath>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-setting</artifactId>
    <version>5.8.29</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/libs/hutool-setting-5.8.29.jar</systemPath>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-json</artifactId>
    <version>5.8.29</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/libs/hutool-json-5.8.29.jar</systemPath>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-log</artifactId>
    <version>5.8.29</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/libs/hutool-log-5.8.29.jar</systemPath>
</dependency>

然後執行 javafx:jlink,這時就能正常打包程式碼了

module-info 基礎知識補充

  • module :宣告模組及其名稱。
  • open module :宣告整個模組允許反射訪問。
  • requires :宣告對其他模組的依賴。
  • requires transitive :宣告傳遞性依賴。
  • exports :公開包,使得其他模組可以訪問。
  • opens :開放包,允許反射訪問。
  • opens to :開放包,僅對指定模組允許反射訪問。
  • uses :宣告對服務介面的依賴。
  • provides ... with :宣告服務介面的實現提供。

相關文章