JavaFX11:使用Gradle创建jar文件

JavaFX11:使用Gradle创建jar文件,gradle,jar,java-11,javafx-11,Gradle,Jar,Java 11,Javafx 11,我正在尝试将JavaFX项目从8Java版本升级到11版本。当我使用“run”Gradle任务()时,它可以工作,但是当我构建(使用“jar”Gradle任务)并执行(使用“java-jar”)一个jar文件时,会出现消息“Error:JavaFX运行时组件丢失,并且需要运行此应用程序” 这是我的build.gradle文件: group 'Project' version '1.0' apply plugin: 'java' sourceCompatibility = 1.11 reposi

我正在尝试将JavaFX项目从8Java版本升级到11版本。当我使用“run”Gradle任务()时,它可以工作,但是当我构建(使用“jar”Gradle任务)并执行(使用“java-jar”)一个jar文件时,会出现消息“Error: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'
        ]
    }
}
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
}
rootProject.name = 'Project'

你知道我应该怎么做吗?

使用Java/javafx11,shadow/fat jar将无法工作

正如你所看到的:

此错误来自java.base模块中的
sun.launcher.launchelper
。原因是主应用程序扩展了
应用程序
,并且有一个Main方法。如果是这种情况,
LaunchHelper
将检查javafx.graphics模块是否作为命名模块出现:

这意味着,毕竟,您必须安装JavaFXSDK(每个平台)来运行使用maven central的JavaFX依赖项的jar

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

您还可以使用Javapackager为每个平台生成安装程序。请参阅,这将为Java12生成一个打包程序

最后,还有一个与Java 11/JavaFX 11一起使用的Javapackager的实验版本:

编辑

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

如果您有一个带有应用程序类的
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


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

对于Java/JavaFX11,shadow/fat jar将无法工作

正如你所看到的:

此错误来自java.base模块中的
sun.launcher.launchelper
。原因是主应用程序扩展了
应用程序
,并且有一个Main方法。如果是这种情况,
LaunchHelper
将检查javafx.graphics模块是否作为命名模块出现:

这意味着,毕竟,您必须安装JavaFXSDK(每个平台)来运行使用maven central的JavaFX依赖项的jar

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

您还可以使用Javapackager为每个平台生成安装程序。请参阅,这将为Java12生成一个打包程序

最后,还有一个与Java 11/JavaFX 11一起使用的Javapackager的实验版本:

编辑

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

如果您有一个带有应用程序类的
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


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

[编辑:有关JavaFX的最新版本,请查看我的第二个答案]

如果有人感兴趣,我找到了一种为JavaFX11项目(带有Java9模块)创建jar文件的方法。我只在Windows上测试了它(如果应用程序也是针对Linux的,我认为我们必须在Linux上执行相同的步骤才能获得针对Linux的JavaFXJAR)

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

module-info.java文件:

module Project.main {
    requires javafx.controls;
    exports main;
}
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'
        ]
    }
}
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
}
rootProject.name = 'Project'
settings.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'
        ]
    }
}
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
}
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"

[编辑:有关JavaFX的最新版本,请查看我的第二个答案]

如果有人感兴趣,我找到了一种为JavaFX11项目(带有Java9模块)创建jar文件的方法。我只在Windows上测试了它(如果应用程序也是针对Linux的,我认为我们必须在Linux上执行相同的步骤才能获得针对Linux的JavaFXJAR)

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

module-info.java文件:

module Project.main {
    requires javafx.controls;
    exports main;
}
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'
        ]
    }
}
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
}
rootProject.name = 'Project'
settings.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'
        ]
    }
}
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
}
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"

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

使用这些插件,您可以:

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

如果你想要一个例子,我就做了。

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

使用这些插件,您可以:

  • 创建包含所有nee的可分发zip文件