使用openjdk 11 + runtime打包JavaFX应用程序束

13

我创建了一个依赖于OpenJDK 11和JavaFX的小型HelloWorld Java应用程序。该应用程序打包在一个jar文件中,只有在我单独安装了Java 11和JavaFX时才能运行。

现在我想将我的jar转换成自包含的Java应用程序,其中包括JavaFX和完整的Java运行时环境。这将使我能够在不安装OpenJDK 11(这会带来技术障碍,如正确设置PATH等)的情况下运行我的应用程序。

我可以找到有关在Java 10上创建自包含Java应用程序的信息,但我找不到有关捆绑Java应用程序与OpenJDK 11和JavaFX的信息。

我该如何使用OpenJDK 11和JavaFX一起发布自包含的Java应用程序(其中包括Java运行时环境)?


请查看 http://launch4j.sourceforge.net/。 - Luke
使用jlink制作自定义镜像。但必须仅使用模块化Java应用程序。 - mr mcwolf
我也在为此苦恼。我本来期望JavaFX运行时能够以跨平台的方式(例如.jar! /../lib)解决这个问题,因为这对于依赖该技术的应用程序开发人员来说是很常见的。 - tresf
好的,我搞定了。现在发布答案。 - tresf
5个回答

16

模块化Java、jlink和jpackage

请遵循这些教程,它们可以在JavaFX的新主页上找到,该项目已经从Oracle中分离出来并交由Gluon管理。

您需要使用模块化Java编写代码,并将JavaFX 11添加为项目依赖项。使用新的链接/打包工具将JDK的子集捆绑到应用程序中。

了解以下内容:

不再需要JRE

Oracle不再打算让终端用户安装JRE或JDK。浏览器中的Java AppletsJava Web Start应用程序交付正在逐步淘汰,使终端用户不再需要JRE。基于Java的应用程序预计将捆绑自己的Java实现。唯一有意识安装JDK的人将是开发人员和服务器端系统管理员。

重要提示:

  • 清楚地了解OpenJDK项目的性质,如维基百科所述
  • 了解Oracle已经承诺在其自有品牌的Oracle JDKOpenJDK项目之间实现功能平衡。这个承诺包括先前商业功能的捐赠,例如Flight RecorderMission Control
  • OpenJFX是JavaFX的开源开发,是OpenJDK项目的一部分。 OpenJFX可能包含或不包含在OpenJDK的构建中。 Java规范不要求JavaFX功能。
  • 至少有两个供应商提供其JDK产品版本,其中包含OpenJFX库:
    • Azul Systems的ZuluFX
    • BellSoft的LibericaFX
  • 阅读Oracle于2018-03发布的白皮书Java Client Roadmap Update
  • 阅读Java社区重要成员撰写的白皮书Java Is Still Free

这里有一个流程图,可以帮助您在提供Java 11实现的各个供应商之间进行选择和决策。

Flowchart guiding you in choosing a vendor for a Java 11 implementation


Motivations in choosing a vendor for Java


1
我认为“Java仍然免费”白皮书可以在https://medium.com/@javachampions/java-is-still-free-2-0-0-6b9aa8d6d244找到。 - Mihai B
2
@MihaiB 谢谢,我已经更新了答案并添加了你提供的链接。顺便说一下,如果你有兴趣的话,Stack Overflow 邀请你自己进行这样的编辑。 - Basil Bourque

2

你可以将整个JDK与你的应用程序捆绑在一起,并创建一个批处理脚本来使用捆绑的JDK运行你的应用程序。我知道这种方法会显著增加你的发布文件大小,但另一种选择是要求用户自己安装JDK,这对于非技术人员来说并不容易。或者你可以同时发布两个版本,一个带有捆绑的JDK,一个没有。


1

1

(使用jdk14)

首先要知道,为了使用jlink你的主要jar包应该是一个模块。

怎么做? 假设你有一个maven项目。你只需要在src/main/java目录下添加module-info.java文件,并确保你require了你的应用所需的模块,同时export包含主类的包。在大多数情况下,当缺少requires时会出现编译时错误。请记住,非模块依赖项会成为自动模块

你可以使用maven的copy-dependencies来确保所有依赖项都被复制到target/lib目录下,在运行mvn package时一同打包。

下一步:jlink

由于jlink maven插件仍处于alpha阶段,你可以使用命令行方式。

注意:

  • jlink将创建一个自包含的捆绑目录,其中包含
    • 主应用程序模块
    • 应用程序依赖项
    • 需要的jdk模块
    • 应用程序启动器(可选)
  • jlink捆绑目标一次只能针对一个平台。默认情况下,它是当前平台
  • javafx运行时模块也是特定于平台的。但由于它们不是jdk的一部分,因此我们需要始终提供包含它们的模块路径
  • javafx运行时模块可以通过使用相应的目标平台分类器(win / linux / mac)从web下载从maven repo下载
  • jlink还可以创建跨平台捆绑包。只需将目标平台模块包含在--module-path中(例如,在Linux上:下载Windows jdk / javafx并将其 dirs添加到module-path中)。

jlink 命令

情况 1: 构建和目标 平台 相同

注意:除非使用 Maven(copy-dependencies)将所需的 JavaFX 依赖项复制到 lib/ 下,否则需要提供 /path-to/javafx-mods 到您的 modulepath


jlink --launcher run=jdk14Example/com.example.javafx.app.Main \
--module-path ./lib:javafx-jdk14-example-1.0.0.jar:/path-to/javafx-mods \
--add-modules=jdk14Example --output app-bundle

案例2:构建和目标平台不同。
# Building from linux for windows
jlink --launcher run=jdk14Example/com.example.javafx.app.Main  \
--module-path ./lib:javafx-jdk14-example-1.0.0.jar:/path-to/jdk-win/jmods:/path-to/javafx-mods-win \
--add-modules=jdk14Example --output app-bundle

结论:

在以上两种情况下,您将获得一个包含完整应用程序的目录,该应用程序可以在未安装java/javafx的工作站上运行。

# if jlink targeted linux
app-bundle/bin/run

# if jlink targeted windows
app-bundle/bin/run.bat

# if jlink targeted mac
app-bundle/bin/run


1

本地库

我遇到的一个挑战是让JavaFX知道它自己的本地库(.dll.dylib.so等)。幸运的是,只需使用System.setProperty(...)设置java.library.path即可轻松加载dylibs。

历史上,在Java中设置此变量被认为是无意义的,因为对于类加载器来说太晚了(不如-Djava.library.path),而使用反射强制设置它是Java 10之后的禁止安全违规... 幸运的是,JavaFX 自然地支持此变量,在设置后将会自动识别它而无需任何违规或黑客手段。

// Detect the path to the currently running jar
String jarPath = new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath();

// Fix characters that get URL encoded when calling getPath()
jarPath = URLDecoder.decode(jarPath, "UTF-8");

String parentFolder = new File(jarPath).getParent();

// If libglass.dylib is next to the jar in a folder called "/bin"
System.setProperty("java.library.path",  parentFolder + "/bin");

// ... then make any javafx calls

Java库

当然,.jar文件也需要可访问。我像处理任何Java捆绑包一样将它们压缩成分发包(制作单个大的.jar文件)。

这些.jar文件应与所有JavaFX 11分发版本保持一致,并应相应地捆绑。

javafx-swt.jar
javafx.base.jar
javafx.controls.jar
javafx.fxml.jar
javafx.graphics.jar
javafx.media.jar
javafx.swing.jar
javafx.web.jar

Java 8 兼容性

使用上述技术进行的对 Java 8 的初步测试结果是积极的。目前,我正在使用 Java 版本检测(不包括在上述示例中),并且仅为 Java 11 或更高版本设置 java.library.path。Java 8 在个人使用方面于2019年12月EOL(商业使用方面于2019年1月EOL),因此在客户从一个 LTS 版本迁移到另一个版本时提供兼容性非常重要。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接