让 ScalaFx 在 JDK 8 和 11 上工作

Make ScalaFx working on both JDK 8 and 11

令我沮丧的是,我已经意识到 my open source ScalaFX application does not run with JDK 11. The application was developed with JDK 8. I have read about a JFX module and I can see there exist versions of ScalaFX dedicated to JDK 11 and newer (ScalaFX 12)。按照我的理解,它并没有达到我想要的效果——我必须为 JDK 8 和 JDK 11.

构建不同版本的应用程序

有没有办法让单个 ScalaFX 应用程序同时在 JDK 8 和 11 上运行?

更新:我对原来的答案进行了简化和扩展。

为了解决您的主要问题,我相信应该可以编写一个针对 JavaFX[= 的单个 ScalaFX 8 应用程序200=] 8 和 JavaFX 9+,前提是您坚持前者定义的 API。因此,我建议使用 Java 8 JDKScalaFX 编译您的应用程序/JavaFX 8. 如果你使用 Java 9+ JDK,或针对任何高于 8 的 ScalaFX 版本构建,那么您将无法 运行 您的应用程序 Java/JavaFX8运行-time.

(如果你想支持ScalaFX/JavaFX的新功能,那么你别无选择,只能以它们出现的特定版本为目标,如果您仍然需要支持旧版本,这可能需要您生成应用程序的多个版本。既然您已经声明这不是您的目标,我将以 ScalaFX 为目标/JavaFX 8 这里。)

请注意,Java 版本 8 和 11 是长期支持LTS) 发布; Java 版本 9、10 和 12 不是,而且更前沿。

如您所知,Java 8 和随后的 Java[=200= 之间存在根本差异] 版本,在 Java 中引入 Jigsaw 模块 9. Scala 支持模块仍然非常原始,目前或多或少假装它们不存在,这在这种情况下实际上有帮助。

需要克服的主要问题是在[=327时配置Java虚拟机JVM) =]宁你的应用程序。

顺便说一句,请注意我使用术语 JavaFX 来包含 OpenJFX,它是 [=94] 的开源版本=]JavaFX.

当 运行使用 JavaFX8 连接您的应用程序时,将有一个 JAR 文件名为 jfxrt.jar,您需要做的就是确保您的类路径中有该文件。这基本上与您的应用程序始终具有的要求相同,因此您应该在这里做得很好。顺便说一句,JavaFX 8 应该仍然可以与 Java 9+ 一起正常工作,因为 Java 9+ 需要保持与旧的非模块化库的兼容性。

然而,当 运行将您的应用程序与 JavaFX 9+ 结合使用时,事情会变得更加有趣...

首先,根据经验,如果您使用的是 JavaFX 版本 X(其中 X 是高于 8 的某个版本),您通常还必须使用版本等于或高于 X[= 的 JVM 200=]。也就是说,例如,您应该可以 运行ning JavaFX 9 使用 JVM 版本 11。但是,您通常不能期望将 JavaFX 11 与 JVM 版本 9.

一起使用

虽然 JavaFX 8 有一个包含整个库的 JAR 文件,但版本 9+ 被打包到许多独立的模块,每个模块都有自己的 JAR 文件:

  • javafx-base
  • javafx-controls
  • javafx-fxml
  • javafx-graphics
  • javafx-media
  • javafx-swing
  • javafx-web.

如果您不需要,比如说,javafx-swing 中定义的元素,那么您可以忽略相应的 JAR 文件。但是,为了安全起见,您可能需要要求所有这些,这就是我在这里要做的。

您需要告诉 JVM 这些是模块,并在应用程序 运行s.

时加载它们

例如,假设您已将 JavaFX 11 安装到计算机上名为 C:\JavaFX11 的目录中,这样库的 JAR 个文件位于 C:\JavaFX11\lib。假设我有一个环境变量 JAVAFX_HOME 来标识后一个目录。当 运行 应用程序时,您现在需要将以下参数传递给 JVM

 --module-path %JAVAFX_HOME%
 --add-modules javafx.base,javafx.controls,javafx.fxml,javafx.graphics,javafx.media,javafx.swing,javafx.web

这些选项仅对 JVM 版本 9 或更高版本有效。 (显然,这也适用于 Windows;您需要为其他平台做类似的事情。)

如果您这样做,那么您可能会看到一个错误 Error: JavaFX runtime components are missing, and are required to run this application。 (如果您看到不同的东西,您能否更新您的问题以显示 ScalaFX 8 应用程序在 JDK 下的 运行 的结果11?)

当然,诀窍是让 运行 您的应用程序的脚本检测安装的 JavaFX 的版本。如果您要求您的用户定义一个 JAVAFX_HOME 环境变量来指定他们的 JavaFX 库安装位置,这可能会有所帮助。如果它包含一个名为 jfxrt.jar 的文件,那么您使用的是版本 8,否则是版本 9+。在后一种情况下,您可能还想在提供其他参数之前验证您使用的是版本 9+ JVM

如果您违反任何打包为模块的库中定义的任何模块限制,仍然存在一些潜在的香蕉皮(可能导致 运行 时错误)。如果你坚持发布的 JavaFX 8 API 规范,并且不要尝试访问私有实现 类,你应该是好的。

更新 2:处理更多评论。

I am not interested in any newer features, my only goal is to make old Java 8 application runnable with new JDKs, including JDK 11 and 12. If I would like to distribute JFX 8 (a single jar jfxrt.jar you have mentioned), how do I get it? I found maven artifacts only for JFX 11 and newer, downloads at gluonhq.com/products/javafx also start with 11)

如您所知,JavaFX 8 作为 Oracle Java 的一部分捆绑在一起8 运行-时间环境 (RTE)。这个 Oracle 版本的 JavaFX 在任何情况下都是专有的,与您的产品一起分发可能是不明智的。

此外,您可能已经知道,OpenJDK 8 RTE 版本 not bundle JavaFX,让用户自己安装 OpenJFX 8。在后一种情况下,此版本的任何存储库中都没有可用的工件,因为直到模块化才可以打包 JavaFX.

我的理解是 Gluon 最近才开始支持 JavaFX(如 OpenJFX),提供独立下载,并在 Maven Central Repository, only as of JavaFX 11. Prior to them stepping up, it was the responsibility of the Oracle controlled OpenJFX project. The latter still hosts the official Mercurial-based code base 上提供可用的打包工件,但已将其大部分链接(包括下载链接)更新到 Gluon 站点.

谷歌搜索OpenJFX 8 下载”看来,可用的选项不多,但构建 your own version from the sources is one possibility. Using something like the wayback machine是另一个。如果您 运行 正在使用 Linux,许多发行版仍会在其存储库中提供 OpenJFX 8 个版本。

也许你可以向 Gluon 报告缺少 JavaFX 8 运行-time 和看看他们是否准备发布一个?

然而,有一个更大的问题正在酝酿:你面临的主要问题是 JavaFX 不是 纯 Java 图书馆。它包含许多从 JavaFX JAR 文件中引用的特定于平台的库。仅捆绑您自己的 jfxrt.jar 版本是行不通的,因为每个平台的基础库都将丢失。

即使你能克服这个问题,你也需要为每个支持的平台发布不同的版本(Windows 64 位, Windows 32 位、MacOs 等)与 OpenJFX.

的适当捆绑版本

您可能需要考虑放弃对 JavaFX 8 的支持,因为您随后可以将依赖于平台的库依赖项添加到您的应用程序构建中,并使用 SBT 插件,例如 sbt-native-packager 将您的 JavaFX 依赖项捆绑到特定于平台的安装包中。

如果您仍然需要支持 JavaFX 8,您目前别无选择,只能依靠用户提供 JavaFX自己。

I have tried a JDK 8 directory containing jfxrt (which is Java\jdk1.8.0_161\jre\lib\ext for me) to the classpath of the application running under JDK 11 and the application was unable to start, with the exception "java.lang.RuntimeException: Exception in Application start method" with additonal information "Caused by: java.lang.NoClassDefFoundError: sun/misc/SharedSecrets"

如果没有查看您的代码、它是如何 运行 以及应用程序的完整输出,很难确切地说出是什么导致了这个问题。您能否更新您的问题以包含该信息?