JavaFX 警告:不支持的 JavaFX 配置:类是从“未命名模块@...”加载的

Jon*_*Jon 37 java javafx

我只是下载了 JavaFX 并进行了设置,我没有做任何其他事情。我运行了示例代码,尽管所有内容都已编译,但还是弹出了警告。我正在使用 IntelliJ。

这是在 Main.java 中:

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 300, 275));
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是在sample.fxml中:

<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane fx:controller="sample.Controller"
          xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
</GridPane>
Run Code Online (Sandbox Code Playgroud)

运行时,一切都会编译,会弹出窗口,但我收到标题中所述的警告。

我以前从未使用过 JavaFX,所以我不确定在哪里可以找到这个模块。

Sla*_*law 54

长话短说:

确保 JavaFX 被解析为命名模块

  • 非模块化应用:

    java --module-path <path-to-fx> --add-modules <fx-modules> ...
    
    Run Code Online (Sandbox Code Playgroud)
  • 模块化应用:

    java --module-path <path> --module <main-module>/<main-class> [args...]
    
    Run Code Online (Sandbox Code Playgroud)

    其中<path>包括您的模块以及 JavaFX。

  • 使用包含 JavaFX 的 JDK 发行版(有关更多信息,请参阅下面的答案)。可能还是需要用--add-modules

或者,如果您决定接受不受支持的配置,则可以忽略该警告。据我所知,从 JavaFX 21 开始,目前没有任何问题。


背景

此警告与 Java 9 中引入的Java 平台模块系统(JPMS)相关。如果您不熟悉模块,那么我建议阅读了解 Java 9 模块以获取简要概述。

JPMS 带来了模块路径,它现在与类路径一起存在。当从类路径加载类时,它们成为所谓的未命名模块的一部分。而模块路径上的类包含在模块中,因此被加载“到”命名的模块中。命名模块可能是自动的,也可能不是自动module-info的,分别取决于它是否有描述符。


警告

JavaFX 仅支持作为命名模块加载。换句话说,JavaFX 仅支持从模块路径加载,而不支持从类路径加载。从版本 9 开始,即框架模块化时,这一点就隐含地成立了。但现在,从版本 16 开始,如果 JavaFX 检测到它是从类路径加载的(即位于未命名模块中),则会发出警告。请参阅JDK-8256362


解决方案

解决方案是确保从模块路径加载 JavaFX 并将其解析为命名模块。注意 JavaFX 由七个模块组成,从其文档可以看出。

如何确保 JavaFX 被解析为命名模块可能取决于您的环境(例如,非模块化与模块化应用程序、外部 JavaFX SDK 等)。下面的示例均使用命令行来展示如何设置所需的参数。但是,如果您使用 IDE(例如 NetBeans、IntelliJ、Eclipse)和/或构建工具(例如 Maven、Gradle)并且不确定在哪里设置这些参数,请查看JavaFX 11+ 入门

请注意,Gradle 和 Maven 都有插件,可以更轻松地使用 JavaFX(即它们为您处理所有配置)。

非模块化应用

如果您的代码是非模块化的,那么您就没有module-info描述符。这意味着将 JavaFX 添加到模块路径是不够的;您还必须强制 Java 解析 JavaFX 模块。这可以通过论证来完成--add-modules

例如:

java --module-path <path-to-fx> --add-modules javafx.controls ...
Run Code Online (Sandbox Code Playgroud)

javafx.controls请注意,我只在参数中包含模块--add-modules。这是因为它需要javafx.basejavafx.graphics模块,这意味着它们将被隐式拉入。

如果您需要其他模块,那么您还需要将它们包含在--add-modules参数中,并用逗号分隔。

另外,请注意,在这种情况下,可以将代码和其他非模块化依赖项放在类路径上。重要的部分是 JavaFX 是从模块路径加载的。

模块化应用

如果您的代码是模块化的,那么您拥有一个module-info描述符。该描述符将具有必要的requires指令。在这种情况下,您不再需要使用--add-modules,但您确实需要确保通过 来将应用程序作为模块启动--module

例如:

module app {
  // transitively requires javafx.base and javafx.graphics
  requires javafx.controls;

  // export javafx.application.Application implementation's package
  // to at least javafx.graphics
  exports com.example.app to javafx.graphics;
}
Run Code Online (Sandbox Code Playgroud)
java --module-path <path> --module app/com.example.app.Main [args...]
Run Code Online (Sandbox Code Playgroud)

请注意,您不仅需要将 JavaFX 放在模块路径上,还需要将您自己的模块放在模块路径上。

使用包含 JavaFX 的 JDK 发行版

从 Java 11 开始,JavaFX 不再包含在 Oracle 提供的 JDK 中。但 Oracle 并不是唯一一家基于 OpenJDK 的 Java 供应商。有些供应商提供包含 JavaFX 的 JDK 发行版。其中至少有两个是:

另请注意,您可以使用jilnk它来创建包含 JavaFX 模块的 Java 运行时

当 JavaFX 包含在 JDK 中时,它现在像所有其他 Java 模块一样成为运行时映像的一部分(例如java.base)。这意味着它将自动作为命名模块加载,您不再需要手动将 JavaFX 放在模块路径上。

忽略警告

据我所知,如果从类路径加载 JavaFX(从版本 21 开始),目前不会出现任何问题。这意味着至少现在,另一种选择是忽略警告。尽管由于这种配置而造成的任何问题都不太可能被 JavaFX 开发人员修复。

不过,我建议至少将 JavaFX 放在模块路径上(如果您可以轻松做到这一点)。

请注意,当从未命名模块(即类路径)加载 JavaFX 时,主类不能javafx.application.Application. 您必须定义一个单独的主类,其 main 方法仅启动 JavaFX 应用程序。对于 JavaFX 9 至 JavaFX 21(至少包括 JavaFX 21)都是如此。


部署

以上内容在开发过程中主要有帮助。那么部署呢?如何将 JavaFX 包含在您的应用程序中并从模块路径加载 JavaFX?

请注意,本节仅旨在列出一些部署选项。它没有提供细节。互联网上有许多现有资源可以获取更多信息。有关一些链接,请参阅JavaFX 标签信息页面的“打包”部分。

要求客户端安装有 JavaFX 的 JRE

这与之前的“使用包含 JavaFX 的 JDK 发行版”本质上相同。但是,客户端不应安装 JDK,而应只安装 JRE。请注意,JDK 只是 JRE,但包含开发工具。

除非您控制客户端计算机,否则这种方法可能不切实际。

分发独立的应用程序

自包含的应用程序包括您的应用程序代码、所有库代码和 JRE 本身。这意味着客户端甚至不需要安装 Java。

创建独立应用程序的一种方法是通过jlink和/或jpackage用户指南)。请注意,jlink仅适用于非自动模块,而jpackage可以与非模块化应用程序一起使用,也可以与模块化和非模块化依赖项的混合一起使用(例如,将 JavaFX 放在模块路径上,但将其他所有内容放在类上) -小路)。然而,与 不同的是jlink,该jpackage工具不支持跨平台编译。

另一个选择是GraalVM 的原生镜像

可执行的“胖”JAR 文件

历史上,Java 应用程序作为可执行 JAR 文件进行分发。所谓的“fat”、“uber”或“shadowed”JAR 文件只是将应用程序中嵌入的所有依赖项嵌入到同一个 JAR 文件中。您可以使用此方法将 JavaFX 透明地包含在您的应用程序中。请注意,这需要客户端安装适当版本的 Java。

使用这种方法是隐式不支持的。JAR文件规范不支持每个 JAR 文件有多个模块,也没有定义任何指定模块路径的方法。此外,可执行 JAR 通常是通过-jar将所有内容都放在类路径上的开关启动的。所有这一切都意味着 JavaFX 将从未命名的模块中加载。简而言之,这种方式本质上是“忽略警告”的部署解决方案。

关于主类的相同规定也适用于此,就像前面的“忽略警告”部分一样。