如何使用 Maven Assembly 插件为 JavaFX 项目创建一个一体化 jar?

ktm*_*124 1 java javafx maven maven-assembly-plugin

我有一个非常简单的 Java 类src/main/java/webbrowser/WebBrowser.java

package webbrowser;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.scene.web.WebEngine;
import javafx.stage.Stage;

public class WebBrowser extends Application {

    @Override
    public void start(Stage stage) {
        WebView browser = new WebView();
        WebEngine webEngine = browser.getEngine();
        webEngine.load("http://www.oracle.com");
        Scene scene = new Scene(browser, 1200, 900);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }

}
Run Code Online (Sandbox Code Playgroud)

我有一个pom.xml列出 JavaFX 依赖项并配置程序集插件的文件:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>webbrowser</groupId>
    <artifactId>WebBrowser</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>WebBrowser</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>18-ea+9</version>         
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>18-ea+9</version>
        </dependency>
    </dependencies>
  
    <build>
        <finalName>WebBrowser</finalName>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>single</goal>
                            </goals>                            
                        </execution>
                    </executions>
                    <configuration>
                        <archive>
                            <manifest>
                                <mainClass>
                                    webbrowser.WebBrowser
                                </mainClass>
                            </manifest>
                        </archive>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                    </configuration>
                </plugin>       
            </plugins>
        </pluginManagement>
    </build>  
</project>
Run Code Online (Sandbox Code Playgroud)

当我在命令行上运行时,mvn clean install assembly:single它显示“BUILD SUCCESS”,并且日志中没有警告或错误。

然后我运行:

% java -jar target/WebBrowser-jar-with-dependencies.jar
Error: JavaFX runtime components are missing, and are required to run this application
Run Code Online (Sandbox Code Playgroud)

由于某种原因,mvn assembly:single 插件并未包含所有 JavaFX 依赖项。

但是,当我在命令行上运行它时,它工作正常并启动应用程序:

% java --module-path $JAVAFX --add-modules javafx.controls,javafx.web -jar target/WebBrowser-jar-with-dependencies.jar
Run Code Online (Sandbox Code Playgroud)

问题

  1. 为什么 assembly:single goal 没有将 javafx.controls 和 javafx.web 依赖项打包在里面target/WebBrowser-jar-with-dependencies.jar
  2. 如何配置 Assembly 插件以将 javafx.controls 和 javafx.web 依赖项包含在一个一体化的可运行target/WebBrowser-jar-with-dependencies.jar文件中?

jew*_*sea 6

建议:改变您的包装方式

不要尝试为您的应用程序创建单个 jar。

以下是包装资源的链接:

我建议您查看那里的信息,然后为您的应用选择并实施适当的(和不同的)包装解决方案。

了解错误消息:缺少 JavaFX 运行时组件

这意味着您的应用程序无法访问 JavaFX 运行时。JavaFX 模块应该位于您的模块路径上,如果您将它们放入应用程序 jar 中,目前这是不可能的。

有关此问题和一些基本解决方案的一些信息,请参阅 Eden 编码教程(尽管它不涵盖一般的打包应用程序,因为这是一个非常复杂的主题):

解决您的具体问题

1 为什么javafx依赖项不在jar-with-dependency.jar中

我复制了您的代码,并在装有 OpenJDK 17 的 Windows 计算机上进行了本地尝试。

构建了一个带有依赖项的 jar,并且 javafx Windows包含在该 jar 中。您可以通过运行jar tvfjar 文件来看到这一点。它包括 JavaFX 平台类文件和 Windows 本机 DLL。

但是当构建 jar 时,有一条信息消息:

[INFO] module-info.class already added, skipping
Run Code Online (Sandbox Code Playgroud)

这是关键。每个模块化 JavaFX jar 都有自己的模块信息文件,这是它在模块化环境中运行所必需的。当跳过某些内容时,模块化就会被破坏,并且应用程序将不再运行在模块路径之外。

如果 Maven 程序集插件足够聪明,能够识别依赖项中的模块化 jar,并在要求创建带有依赖项的单个 jar 时明确发出警告,那就太好了,因为java 运行时目前不支持多模块 jar

要了解有关当前为何无法将多个 Java 平台模块放入单个 jar 中的更多信息,请参阅:

2 创建带有依赖项的 jar

您可以从程序集插件切换到阴影插件并使用以下解决方案:

但您不必这样做,如果您确实想要这样做,您可以继续使用程序集插件。

如果您这样做,请了解链接中的警告,即您将在类路径而不是模块路径之外的不受支持的配置中运行 JavaFX。

要使用程序集执行此操作:

  1. 添加一个启动器类来欺骗 JavaFX 运行时,使其从类路径而不是模块路径运行。

    package webbrowser;
    
    public class WebBrowserLauncher {
        public static void main(String[] args) {
            WebBrowser.main(args);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 更改为程序集定义的主类以引用新类:

    <mainClass>
        webbrowser.WebBrowserLauncher
    </mainClass>
    
    Run Code Online (Sandbox Code Playgroud)
  3. 如果您希望允许应用程序在与您构建应用程序的操作系统不同的操作系统上运行,请将这些平台的依赖项添加到您的 pom.xml 中。有关所需附加依赖项的示例,请参阅:

我进行了更改 1 和 2 并对其进行了测试,并且以这种方式打包的应用程序可以为我运行。我没有费心更改 3,因为此时我无法轻松测试它。

执行命令及输出:

java -jar target/WebBrowser-jar-with-dependencies.jar
Jan 21, 2022 7:59:51 PM com.sun.javafx.application.PlatformImpl startup
WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module @41dc7fe'
Run Code Online (Sandbox Code Playgroud)

存在警告是因为 JavaFX 是从类路径而不是模块路径加载的。

将多个 jar 组装成 zip 或 tar 存档

Assembly 仍然可以是在构建过程中使用的有用插件,但目的不同。

如果您确实想坚持使用组装方法,您可以:

  1. 创建一个没有依赖项的jar 。
  2. 使用程序集创建一个 lib 目录,其中包含您需要支持的平台的依赖项和 JavaFX 模块。
  3. 提供特定于平台的脚本来启动应用程序,该脚本会适当配置类路径和模块路径以使用从存档中提取的库依赖项。
    • 这很方便,因为用户可以在命令行中键入完整的命令,尽管这可能比仅仅执行脚本更痛苦。
  4. 然后,您可以创建一个自定义程序集,将所有这些内容包含在 tar 或 zip 文件中。

然后,用户将能够解压缩您的存档并使用您的启动器脚本来运行应用程序(只要他们预装了适当的 JVM)。

Assembly 对此很有帮助,但 jlink 或 jpackage 可能是首选替代方案,因为这些选项不需要预安装 JVM。

openjfx-maven-plugin 在其jlink功能中具有类似的选项,但包含链接的 java 运行时:

  • jlinkZipName:设置后,创建生成的运行时映像的 zip。