gradle - JavaFX 11 : Create a jar file with Gradle

标签 gradle jar java-11 javafx-11

我正在尝试将 JavaFX 项目从 Java 8 版本升级到 11 版本。当我使用“run”Gradle 任务( I followed the Openjfx tutorial )时它可以工作,但是当我构建(使用“jar”Gradle 任务)并执行(使用“java -jar”)jar 文件时,消息“错误:JavaFX缺少运行时组件,需要运行该应用程序”。

这是我的 build.gradle 文件:

group 'Project'
version '1.0'
apply plugin: 'java'
sourceCompatibility = 1.11

repositories {
    mavenCentral()
}

def currentOS = org.gradle.internal.os.OperatingSystem.current()
def platform
if (currentOS.isWindows()) {
    platform = 'win'
} else if (currentOS.isLinux()) {
    platform = 'linux'
} else if (currentOS.isMacOsX()) {
    platform = 'mac'
}
dependencies {
    compile "org.openjfx:javafx-base:11:${platform}"
    compile "org.openjfx:javafx-graphics:11:${platform}"
    compile "org.openjfx:javafx-controls:11:${platform}"
    compile "org.openjfx:javafx-fxml:11:${platform}"
}

task run(type: JavaExec) {
    classpath sourceSets.main.runtimeClasspath
    main = "project.Main"
}

jar {
    manifest {
        attributes 'Main-Class': 'project.Main'
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

compileJava {
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
                '--add-modules', 'javafx.controls,javafx.fxml'
        ]
    }
}

run {
    doFirst {
        jvmArgs = [
                '--module-path', classpath.asPath,
                '--add-modules', 'javafx.controls,javafx.fxml'
        ]
    }
}

你知道我应该做什么吗?

最佳答案

对于 Java/JavaFX 11,shadow/fat jar 将不起作用。

正如您可以阅读的那样 here :

This error comes from sun.launcher.LauncherHelper in the java.base module. The reason for this is that the Main app extends Application and has a main method. If that is the case, the LauncherHelper will check for the javafx.graphics module to be present as a named module:

Optional<Module> om = ModuleLayer.boot().findModule(JAVAFX_GRAPHICS_MODULE_NAME);

If that module is not present, the launch is aborted. Hence, having the JavaFX libraries as jars on the classpath is not allowed in this case.

此外,每个 JavaFX 11 jar 都有一个 module-info.class文件,位于根级别。

当您将所有 jar 内容捆绑到一个 fat jar 中时,那些具有相同名称和相同位置的文件会发生什么?即使 fat jar 保留了所有这些,它如何识别为单个模块?

有一个支持此问题的请求,但尚未得到解决:http://openjdk.java.net/projects/jigsaw/spec/issues/#MultiModuleExecutableJARs

Provide a means to create an executable modular “uber-JAR” that contains more than one module, preserving module identities and boundaries, so that an entire application can be shipped as a single artifact.

shadow 插件仍然可以将所有其他依赖项捆绑到一个 jar 中,但毕竟你必须运行类似的东西:

java --module-path <path-to>/javafx-sdk-11/lib \
   --add modules=javafx.controls -jar my-project-ALL-1.0-SNAPSHOT.jar

这意味着,毕竟,您必须安装 JavaFX SDK(每个平台)才能运行使用来自 Maven Central 的 JavaFX 依赖项的 jar。

作为替代方案,您可以尝试使用 jlink创建轻量级 JRE,但您的应用程序需要模块化。

您还可以使用 Javapackager 为每个平台生成安装程序。请参阅http://openjdk.java.net/jeps/343这将为 Java 12 生成一个打包器。

最后,还有一个可与 Java 11/JavaFX 11 配合使用的 Javapackager 实验版本:http://mail.openjdk.java.net/pipermail/openjfx-dev/2018-September/022500.html

编辑

由于 Java 启动器检查主类是否扩展 javafx.application.Application ,在这种情况下,它需要 JavaFX 运行时作为模块(而不是 jar)提供,一种可能的解决方法是添加一个新的 Main 类,该类将成为项目的主类,该类将调用您的 JavaFX 应用程序类。

如果您有 javafx11与 Application 类一起打包:

public class HelloFX extends Application {

    @Override
    public void start(Stage stage) {
        String javaVersion = System.getProperty("java.version");
        String javafxVersion = System.getProperty("javafx.version");   
        Label l = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".");
        Scene scene = new Scene(new StackPane(l), 400, 300);
        stage.setScene(scene);
        stage.show();
    }

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

}

然后你必须将此类添加到该包中:

public class Main {

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

在你的构建文件中:

mainClassName='javafx11.Main'
jar {
    manifest {
        attributes 'Main-Class': 'javafx11.Main'
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

现在您可以运行:

./gradlew run

./gradlew jar
java -jar build/libs/javafx11-1.0-SNAPSHOT.jar

JavaFX from FAT jar

最终目标是将 JavaFX 模块作为模块路径上的命名模块,这看起来是一个测试应用程序的快速/丑陋的解决方法。对于分发,我仍然建议上述解决方案。

关于gradle - JavaFX 11 : Create a jar file with Gradle,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52569724/

相关文章:

android - 项目中两个具有相同applicationId和包名的Android模块

Androidx,如何知道依赖格式/字符串

scala - 有多少 jar 文件被加载到内存中

java - 处理 3 中的非法反射访问错误 (JDK11)

Java 11 包 javax.xml.soap 不存在

android - 尽管minSdkVersion = 15,但无法在具有Android API 15的设备上安装APK

gradle - 如何添加更多文件以通过同步任务复制,但仅适用于一个项目

java - JS 中的 .Jar 文件

java - Mirth 运行旧 JAR 文件代码

java - IntelliJ IDEA - 错误 : A JNI error has occurred, 请检查您的安装并重试