maven 打包非模块化的JavaFX应用程序

oipij1gg  于 2022-10-26  发布在  Maven
关注(0)|答案(1)|浏览(378)

我有一个Java 8应用程序,它使用JavaFX,其中的主类扩展了javafx.Applation.Application。目前,我把它作为一个大jar交付,它在Oracle Java 8上运行得很好。
现在我希望它能够在OpenJDK 11上运行。为了添加JavaFX,我已经将org.Openjfx中的构件添加到类路径中,并将它们包含在FAT JAR中。如果我从命令行启动JAR,我会得到

Error: JavaFX runtime components are missing, and are required to run this
application

我找到了两种可能的方法来解决这个问题:
1.脏的:编写一个特殊的启动器,不扩展应用程序,绕过模块检查。见http://mail.openjdk.java.net/pipermail/openjfx-dev/2018-June/021977.html
1.干净的:在我的命令行中添加--模块-路径和--添加模块。此解决方案的问题在于,我希望我的最终用户能够通过双击应用程序来启动它。
作为一种变通办法,我想知道目前(OpenJDK 11)构建/交付非模块化JavaFX应用程序的可执行FAT JAR的预期方式是什么。有人能帮忙吗?

bxgwgixi

bxgwgixi1#

以下是打包/分发(非模块化)JavaFX 11终端应用程序的几种选择。它们中的大多数都在官方OpenJFX文档中进行了解释。
我将使用this sample作为参考。我还将使用Gradle。使用Maven(不同的插件)也可以做类似的事情,甚至不需要构建工具(但不推荐这样做...)。如今,构建工具是必须的。

胖子罐

这仍然是一个有效的选择,但不是首选的选择,因为它打破了模块化设计并将所有东西捆绑在一起,而且除非您注意这一点,否则它不是跨平台的。
对于给定的样例,您有一个如下所示的build.gradle文件:

plugins {
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.5'
}

repositories {
    mavenCentral()
}

dependencies {
}

javafx {
    modules = [ 'javafx.controls' ]
}

mainClassName = 'hellofx.HelloFX'

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

请注意Launcher类的使用。正如OP提到或解释的here所述,现在需要一个不是从Application扩展的启动器类来创建FAT JAR。
运行./gradlew jar会生成一个FAT JAR(~8MB),其中包含JavaFX类和当前平台的本地库。
您可以像往常一样运行java -jar build/libs/hellofx.jar,但只能在相同的平台上运行。
如OpenJFX文档或此处所述,您仍然可以创建跨平台JAR。
在本例中,我们可以包括三个图形JAR,因为它们具有依赖于平台的代码和库。基本、控件和fxml模块是独立于平台的。

dependencies {
    compile "org.openjfx:javafx-graphics:11.0.1:win"
    compile "org.openjfx:javafx-graphics:11.0.1:linux"
    compile "org.openjfx:javafx-graphics:11.0.1:mac"
}

./gradlew jar现在将生成一个FAT JAR(19MB),可以分发到这三个平台。
(请注意,Media和Web也有依赖于平台的代码/本机库)。
这就像以前在Java 8上一样,但正如我之前所说的,它打破了模块的工作方式,而且它与当今库和应用程序的分发方式不一致。
不要忘记,这些JAR的用户仍然需要安装JRE。

那么,在您的项目中分发一个已包含本机JRE和启动器的定制映像怎么样?
你会说,如果你有一个非模块化的项目,那是行不通的。没错。但是,在讨论jPackage之前,让我们在这里检查两个选项。

运行时插件

badass-runtime-plugin是一个Gradle插件,可以从非模块化项目创建运行时映像。
有了这条build.gradle:

plugins {
    id 'org.openjfx.javafxplugin' version '0.0.5'
    id 'org.beryx.runtime' version '1.0.0'
    id "com.github.johnrengelman.shadow" version "4.0.3"
}

repositories {
    mavenCentral()
}

dependencies {
}

javafx {
    modules = [ 'javafx.controls' ]
}

mainClassName = 'hellofx.Launcher'

runtime {
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
}

当您运行./gradlew runtime时,它将创建一个运行时及其启动器,以便您可以运行:

cd build/image/hellofx/bin
./hellofx

请注意,它依赖于影子插件,并且还需要一个Launcher类。
如果您运行./gradlew runtimeZip,您可以获得这个定制图像的压缩文件,大小约为32.5MB。
同样,您可以将此压缩包分发给具有相同平台的任何用户,但现在不需要安装JRE。
关于为其他平台构建镜像,请参阅targetPlatform

走向模块化

我们一直认为我们有非模块化的项目,这是不能改变的……但如果我们真的改变它呢?

  • 走向模块化*并不是什么大的变化:您添加了一个module-info.java描述符,并在其上包含了所需的模块,即使这些是非模块化JAR(基于自动命名)。

基于相同的示例,我将添加一个描述符:

module hellofx {
    requires javafx.controls;

    exports hellofx;
}

现在我可以在命令行上使用jlink,也可以使用它的插件。badass-gradle-plugin是一个Gradle插件,来自与前面提到的作者相同的作者,它允许创建自定义运行时。
使用此构建文件:

plugins {
    id 'org.openjfx.javafxplugin' version '0.0.5'
    id 'org.beryx.jlink' version '2.3.0'
}

repositories {
    mavenCentral()
}

dependencies {
}

javafx {
    modules = [ 'javafx.controls' ]
}

mainClassName = 'hellofx/hellofx.HelloFX'

你现在可以跑了:

./gradlew jlink
cd build/image/bin/hellofx
./hellofx

或者./gradlew jlinkZip用于压缩版本(31MB),即使没有安装JRE,也可以在相同平台的机器上分发和运行。
如你所见,不需要影子插件或启动器类。您还可以针对其他平台,或包括非模块化依赖项,如question

jPackage

最后,还有一个用于创建可执行安装程序的新工具,您可以使用它来分发应用程序。
到目前为止,还没有GA版本(可能我们必须等待Java 13),但现在有两个选项可以在Java 11或12中使用它:
对于Java/JavaFX 11,在Java 12上的JPackager的初始工作中有一个后端端口,您可以找到here。有一篇关于使用它的好文章here,还有一个使用它的Gradle项目here
在Java/JavaFX 12中,已经有jpackage工具的build 0 version将在Java 13中可用。
这是该工具的一个非常初步的使用:

plugins {
    id 'org.openjfx.javafxplugin' version '0.0.5'
}

repositories {
    mavenCentral()
}

dependencies {
}

javafx {
    version = "12-ea+5"
    modules = [ 'javafx.controls' ]
}

mainClassName = 'hellofx/hellofx.HelloFX'

def java_home = '/Users/<user>/Downloads/jdk-12.jdk/Contents/Home'
def installer = 'build/installer'
def appName = 'HelloFXApp'

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

task jpackage(type: Exec) {
    dependsOn 'clean'
    dependsOn 'copyDependencies'

    commandLine "${java_home}/bin/jpackage", 'create-installer', "dmg",
            '--output', "${installer}", "--name", "${appName}",
            '--verbose', '--echo-mode', '--module-path', 'build/libs',
            '--add-modules', "${moduleName}", '--input', 'builds/libraries',
            '--class', "${mainClassName}", '--module', "${mainClassName}"
}

现在运行./gradlew jpackage将生成一个dmg(65MB),我可以将其分发以安装:

结论

虽然您可以坚持使用传统的FAT JAR,但在迁移到Java 11及更高版本时,一切都应该是模块化的。新的(即将推出)可用的工具和插件,包括对IDE的支持,在这一过渡过程中起到了帮助作用。
我知道我在这里展示了最简单的用例,当尝试更复杂的真实案例时,会有几个问题……但我们应该更好地解决这些问题,而不是继续使用过时的解决方案。

相关问题