为什么我可以在 Swing 中嵌入 JavaFx 但不在没有指定 Fx 库的情况下实例化 JavaFx?

How come I can embed JavaFx in Swing but not instantiate JavaFx without Fx library specified?

我可以在使用 OpenJfx 时将 JavaFx 嵌入到 Swing 应用程序中。但是,一旦我尝试通过 Application.launch(args) 启动本机 Fx 应用程序,就不可能 运行 它不指向解压缩的 JavaFx 库。

在启动 Swing 应用程序并显示一些嵌入的 JavaFx 组件之后,我可以通过一个简单的 java 调用包含在类路径参数中的所有必要的 openjfx 库来启动它,调用看起来像这个:

java -classpath [所有 openjfx 库(也包括平台分类器)] DialogSwing

import java.awt.BorderLayout;
import javax.swing.*;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.effect.Reflection;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;

public class DialogSwing extends JDialog {
    private final JPanel contentPane = new JPanel(new BorderLayout());

    public DialogSwing() {
        setContentPane(contentPane);
        final JFXPanel panel = new JFXPanel();
        Platform.runLater(() -> {
                    try {
                        contentPane.add(panel);
                        StackPane stack = new StackPane();
                        Scene scene = new Scene(stack, 300, 300);
                        Text hello = new Text("Hello");
                        scene.setFill(Color.BLACK);
                        hello.setFill(Color.WHEAT);
                        hello.setEffect(new Reflection());
                        panel.setScene(scene);
                        stack.getChildren().add(hello);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
        );

    }

    public static void main(String[] args) {
        DialogSwing dialog = new DialogSwing();
        dialog.setSize(800, 600);
        dialog.setVisible(true);
        System.exit(0);
    }

    private void onCancel() {
        dispose();
    }
}

在我通过 --module-path 参数以及 prism 库指定指向 openjx lib 目录的完整指针之前,Follow Fx 应用程序不会启动,所以这不起作用:

java -classpath [所有 openjfx 库(也包括平台分类器)] DialogFx

但这是有效的:

java -classpath [所有 openjfx 库(也包括平台分类器)] --module-path /usr/share/openjfx/lib/ -Djava.library.path=/usr/lib/x86_64-linux-gnu/jni/ --add-modules=javafx.controls DialogFx

(指定的库路径包含还需要的工作棱镜库。)

import javafx.application.Application;
import javafx.stage.Stage;

public class DialogFx extends Application {
    public void start(Stage stage) throws Exception {

    }

    public static void main(String[] args) {
        Application.launch(args);
    }

}

最后说明:上面在 Fx Java 调用中描述的差异可能看起来很微妙,但这意味着 运行在没有解压的 Fx 环境的情况下 JavaFx 应用程序是不可能的这让事情变得复杂。但是,如果 JavaFx 可以轻松地在 Swing 环境中使用,为什么需要这样做。我可以想象还有其他方面的影响,比如性能下降,但这当然是另一个话题。

所有这些的标准 JDK 是普通的 OpenJdk 15。

建议

  1. 不要运行JavaFX来自class路径,运行它与相关的JavaFX模块你的模块路径。

  2. 当您有 JavaFX 模块 jar class 时,您也不需要为 JavaFX 本地库指定库目录模块路径上的平台。

    • 平台特定 Java模块路径上的 FX 模块封装了它们使用的本机库。该平台可以在没有额外帮助的情况下找到模块中的本机库。

为什么提出这些建议

来自 class 路径的

运行 不是受支持的配置。参见:

有关详细信息,请参阅这些答案中的文章:

如评论和答案中所述:

  1. A JavaFX 应用程序 class 运行 直接使用 JavaFX classes 在 class 路径上将很快失败一个错误。
  2. 您可以使用单独的 Launcher class hack 来解决这个问题,应用程序将 运行 警告它不受支持(至少 JavaFX 17) .

运行 在 Swing 嵌入式模式下通过 JFXPanel 可能会绕过那些验证 classes 是从模块路径加载的其他应用程序生命周期检查,这可能就是为什么您观察到它看起来只需使用 class 路径上的 JavaFX 模块即可。

但是,来自 class 路径的 运行ning JavaFX 仍然是一个不受支持的配置,因此官方不推荐使用它,即使它在一些执行模式。

官方文档

JavaFX 推荐执行模式的文档在以下位置提供:

常见问题

  1. I am showing JavaFx can be embedded in Swing without any modules specified.

即使它 运行 在这样的配置中,也不支持,建议 JavaFX 模块在模块路径上,而不是 class 路径上。

  1. Running without specifying module-path (native code pointer) an Application can not run, at least in OpenJDK not bundled with JavaFx by default

是的,这是预期的设计。尝试 运行 JavaFX 应用程序而模块路径上没有 JavaFX 模块是不受支持的配置。

As I understand the deeper design goal is to keep Java platform-independent instead of heavy UBER jar packaging with native libraries involved.

没有

JRE 和 JavaFX 等框架需要本机库,不涉及它们不能成为设计目标。相反,构建、打包、部署、执行管道需要考虑本地库。

这就是为什么当 JDK 模块化时,它包括以下功能:

  1. 将本机库封装在模块中(参见 jmod 工具和 Java 模块格式)。
  2. 创建模块的本机映像(请参阅 jlink tool and the Java image format)。
  3. 创建本机安装程序(请参阅 jpackage 工具)。这些都是用于创建和使用 Java 字节码、资源和本机库的工具。

这些都是用于创建和使用 Java 字节码、资源和本机库的工具。

这里有一篇关于 non-modular jar、jmod 和 jimage 包装之间区别的精彩文章:

我鼓励您阅读、研究和试验 Java 模块和相关的工具链,以更好地理解 Java 模块系统。

What strikes me is the hard-coded module-path setting needed. Is there a possibility for Java to rely on system standard paths and check/search for JFX environment available? So it remains flexible in this matter? I think this would be the task of the installed JRE isn't it?

是的,但关键是建议您创建并提供要安装的 JRE 作为应用程序打包的一部分。然后,您可以确定为应用程序正确安装了完整且兼容的 JRE。

推荐的打包机制为jpackage、jlink或native image。这些方法提供 self-contained 包含 JRE 和所有必需模块的应用程序安装程序或捆绑包:您的应用程序代码和资源、依赖库、JRE、JavaFX 模块和启动脚本。

要更好地了解这些分发选项的工作原理,请创建推荐的软件包之一。

另外,正如 Slaw 在评论中指出的那样,您还可以使用包含 JavaFX 的 JDK,并为您的应用程序使用 pre-requisite,您的 end-users 安装一个包含 JavaFX 的 JRE。为 JDK/JavaFX 17 提供这些的一些发行版是 Liberica“Full JDK/JRE”或 Azul“JDK/JRE FX”。

What is the conceptual difference of jlink or jpackage to classpath packaging, why is classpath packaging to be avoided?

模块路径上的项目是 Java 模块,class 路径上的项目不是模块。

两种设置之间存在 lot 的差异:

  1. 封装
    • 模块只能访问所需的模块。
  2. 反射处理
    • 模块必须打开它们的包进行反射。
  3. Class 加载函数
    • class模块路径上的es只在其依赖模块中寻找classes和资源,而不是在class路径上。
  4. 辅助功能
    • 模块范围比 class加载路径 classes.
    • 更严格
  5. 安全
    • 模块具有较低的可见性和攻击面。
  6. 包装
    • 模块引入 jmod 以提供以 jar 格式打包的模块,其中包含模块描述符,并且可以从模块而不是 lib 目录加载本机代码。
    • jimage 格式允许在一个文件中包含多个模块。
  7. 分析工具
    • jdeps 可以深入了解可用模块及其描述符和依赖项。
  8. 构建工具
    • jmod 并且可以创建模块,可选择混合 java 代码和本机代码,其他标准工具如 java 和 javac 是模块感知的,它们的执行模式根据是否你是否在运行class路径上。
  9. 分配机制
    • 可以通过 jimage 链接模块以创建图像或自定义 JRE,但 class 路径上的库不能。
    • class路径上的模块和项目都可以通过 jpackage 打包,它可以基于来自 jlink 的现有 JRE 映像或它创建的包含 [=222] 中的两个模块的现有 JRE 映像创建一个包=] 和您的应用程序及其依赖模块。

有很多差异,有些非常重要。

正因为如此,由于 JavaFX 是作为 JavaFX 11 版本的模块发布的,因此 JavaFX 开发团队仅支持 JavaFX 部署为模块,而不是部署在 class 路径上时。

此 JavaFX 模块的支持策略类似于 JDK/JRE 本身的支持策略。 Java 9 之后,核心JDK 和JRE 系统只能作为模块路径上的模块使用或使用jimage 组装到引导模块路径中。 running the JDK/JRE classes off the classpath via rt.jar or tools.jar is no longer available or supported. So, similarly (post Java 11), the corresponding jfxrt.jar is no longer available or supported 的原始(pre Java 9)配置。与模块不同,那些 JRE/JDK/JavaFX jar 文件仅包含 class 文件,而本机库文件必须单独运送并放置在 lib 路径中(这通常通过整体 JDK/JRE 安装)。模块仍然可以使用 lib 路径上的本机库(我相信),但也可以使用模块中打包的本机库或 JRE 映像本身,这允许它们更 self-contained.