使 ScalaFx 在 JDK 8 和 11 上都能工作

Sum*_*uma 5 java scala scalafx java-11

令我沮丧的是,我意识到我的开源 ScalaFX 应用程序不能与 JDK 11 一起运行。该应用程序是使用 JDK 8 开发的。我已经阅读了有关 JFX 模块的内容,我可以看到存在专用于 JDK 11 和更新版本(ScalaFX 12 )。我理解它的方式并没有达到我想要的 - 我必须为 JDK 8 和 JDK 11 构建不同版本的应用程序。

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

Mik*_*len 5

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

为了解决您的主要问题,我认为应该可以编写一个针对JavaFX 8 和JavaFX 9+的单个ScalaFX 8 应用程序,前提是您坚持使用前者定义的API。因此,我建议使用带有ScalaFX / JavaFX 8的Java 8 JDK编译您的应用程序。如果您使用Java 9+ JDK,或者针对高于 8 的任何ScalaFX版本进行构建,那么您将无法运行您的应用程序具有Java / JavaFX 8 运行时的应用程序。

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

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

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

需要克服的主要问题是在运行应用程序时配置Java 虚拟机( JVM )。

顺便提一下,请注意,我使用术语JavaFX来包括OpenJFX,它是JavaFX的开源版本。

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

但是,当使用JavaFX 9+运行您的应用程序时,事情会变得更有趣...

首先,根据经验,如果您使用的是JavaFX版本X(其中X是高于 8 的某个版本),您通常还必须使用版本等于或高于XJVM。也就是说,例如,您应该可以使用JVM版本 11运行JavaFX 9 。但是,您通常不能期望将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这些是模块,并在应用程序运行时加载它们。

例如,假设您已将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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

更新 2:解决进一步的评论。

我对任何更新的功能都不感兴趣,我唯一的目标是让旧的 Java 8 应用程序可以与新的 JDK 一起运行,包括 JDK 11 和 12。如果我想分发 JFX 8(jfxrt.jar您提到的单个 jar ),我该怎么做得到它?我发现仅适用于 JFX 11 和更新版本的 maven 工件,在gluonhq.com/products/javafx 上的下载也从 11 开始)

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

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

我的理解是Gluon最近才开始支持JavaFX(作为OpenJFX),提供独立下载,并在Maven Central Repository上提供打包的工件,直到JavaFX 11。在他们加强之前,这是Oracle控制的OpenJFX项目。后者仍托管基于Mercurial官方代码库,但已将其大部分链接(包括下载链接)更新到Gluon站点。

谷歌搜索OpenJFX 8 下载”看来,可用的选项并不多,但从源代码构建您自己的版本是一种可能性。使用像回程机这样的东西是另一回事。如果您在Linux运行,许多发行版仍会在其存储库中提供OpenJFX 8 版本。

也许您可以向Gluon报告缺少JavaFX 8 运行时,看看他们是否准备发布一个?

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

即使您可以克服该问题,您也需要为每个受支持的平台(Windows 64 位、Windows 32 位、MacOs 等)提供不同版本的OpenJFX捆绑版本。

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

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

我尝试了一个 JDK 8 目录,其中包含jfxrtJava\jdk1.8.0_161\jre\lib\ext对我来说)在 JDK 11 下运行的应用程序的类路径,但应用程序无法启动,除了“java.lang.RuntimeException:应用程序启动方法中的异常”以及附加信息“由:java.lang.NoClassDefFoundError:sun/misc/SharedSecrets”引起

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