JavaFX 11:使用Gradle创建一个jar文件

时间:2018-09-29 15:15:14

标签: gradle jar java-11 javafx-11

我正在尝试将JavaFX项目从8 Java版本升级到11版本。当我使用“运行” 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'
        ]
    }
}

你知道我应该怎么做吗?

3 个答案:

答案 0 :(得分:21)

使用Java / JavaFX 11,阴影/胖罐子将不起作用。

您可以阅读here

  

此错误来自java.base模块中的sun.launcher.LauncherHelper。原因是Main应用扩展了Application并具有main方法。在这种情况下,LauncherHelper将检查javafx.graphics模块是否以命名模块的形式出现:

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

如果该模块不存在,则启动中止。   因此,不允许将JavaFX库作为jar放在类路径上   在这种情况下。

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

将所有罐子内容捆绑到一个胖罐子中时,那些具有相同名称和位置的文件会如何处理?即使胖子把所有的东西都保存了,那怎么识别为单个模块呢?

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

  

提供一种创建可执行的模块化“ uber-JAR”的方法,该模块包含多个模块,并保留模块标识和边界,以便可以将整个应用程序作为单个工件提供。

shadow插件仍然很有意义,可以将所有 other 依赖项捆绑到一个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)使用,因此可能的解决方法是添加< em> new 主类将成为您项目的主类,而该类将成为调用JavaFX Application类的主类。

如果您有一个带有Application类的javafx11软件包:

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模块作为模块路径上的命名模块,这看起来是测试您的应用程序的快速/丑陋的解决方法。对于分发,我仍然建议上述解决方案。

答案 1 :(得分:3)

如果有人感兴趣,我找到了一种为JavaFX11项目(带有Java 9模块)创建jar文件的方法。我仅在Windows上对其进行了测试(如果该应用程序也适用于Linux,我认为我们必须在Linux上执行相同的步骤才能获得适用于Linux的JavaFX jar)。

我有一个“ Project.main”模块(当我创建Gradle项目时由IDEA创建):

 src
 +-- main
 |   +-- java
     |   +-- main
         |   +-- Main.java (from the "main" package, extends Application)
     |   +-- module-info.java
 build.gradle
 settings.gradle
 ...

module-info.java文件:

module Project.main {
    requires javafx.controls;
    exports main;
}

build.gradle文件:

plugins {
    id 'java'
}

group 'Project'
version '1.0'
ext.moduleName = 'Project.main'
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}"
}

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

jar {
    inputs.property("moduleName", moduleName)
    manifest {
        attributes('Automatic-Module-Name': moduleName)
    }
}

compileJava {
    inputs.property("moduleName", moduleName)
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
                '--add-modules', 'javafx.controls'
        ]
        classpath = files()
    }
}

task createJar(type: Copy) {
    dependsOn 'jar'
    into "$buildDir/libs"
    from configurations.runtime
}

settings.gradle文件:

rootProject.name = 'Project'

和Gradle命令:

#Run the main class
gradle run

#Create the jars files (including the JavaFX jars) in the "build/libs" folder
gradle createJar

#Run the jar file
cd build/libs
java --module-path "." --module "Project.main/main.Main"

答案 2 :(得分:2)

使用最新版本的JavaFX,您可以使用两个Gradle插件轻松分发项目(javafxplugin和jlink)。

有了这些插件,您可以:

  • 创建一个包含所有需要的jar文件的可分发zip文件:它需要执行JRE(使用bash或批处理脚本)
  • 使用Jlink为给定的操作系统创建本机应用程序:不需要JRE来执行它,因为Jlink在分发文件夹中包含“轻型” JRE(仅包括所需的Java模块和依赖项)

如果您想举个例子,我做了an example on bitbucket