java - 了解主类如何影响 JPMS

标签 java javafx java-11 java-platform-module-system javafx-11

我有一个非常基本的 JavaFX 应用程序,如果应用程序类不是主类,它可以完美运行:

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

public class Main {

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

}

public class App extends Application {

    @Override
    public void start(Stage primaryStage) {
        FXMLLoader loader = new FXMLLoader(); // works
    }

}

但是,当我将两者合并在一起时(这是大多数教程中推荐的方式,包括 OpenJFX's official documentation ),模块系统抛出一个 IllegalAccessError(至少在 OpenJDK 11.0.2 上是这样):

public class MainApp extends Application {

    @Override
    public void start(Stage primaryStage) {
        FXMLLoader loader = new FXMLLoader(); // throws IllegalAccessError
    }

    public static void main(String[] args) {
        launch(MainApp.class, args);
    }

}

异常(exception)情况是:

java.lang.IllegalAccessError: class com.sun.javafx.fxml.FXMLLoaderHelper (in unnamed module @0x642c1a1b) cannot access class com.sun.javafx.util.Utils (in module javafx.graphics) because module javafx.graphics does not export com.sun.javafx.util to unnamed module @0x642c1a1b

奇怪的是,我没有主动使用模块系统。我没有将 module-info.java 添加到我的项目中。所以我假设一切都应该导出到任何未命名的模块?但这甚至不是重点。

主要问题是:为什么相同的代码分布在两个类中时表现不同?在这两种情况下,FXMLLoader 使用 com.sun.javafx.fxml.FXMLLoaderHelper,后者又使用 com.sun.javafx.util.Utils。所以我要么在这两种情况下都得到异常(exception),要么都没有。有什么区别?

最佳答案

已经发布了一些可能部分适用于您的问题的答案,但在这里收集它们并在完整答案中展示它们可能会很方便。

应用类

在对 Maven Shade JavaFX runtime components are missing 的回答中我解释了为什么当您使用 Application 类作为主类时,您应该使用模块系统。

总结:

As you can read here:

This error comes from sun.launcher.LauncherHelper in the java.base module (link).

If the main app extends Application and has a main method, the LauncherHelper will check for the javafx.graphics module to be present as a named module:

Optional<Module> om = ModuleLayer.boot().findModule(JAVAFX_GRAPHICS_MODULE_NAME);
if (!om.isPresent()) {
    abort(null, "java.launcher.cls.error5");
}

If that module is not present, the launch is aborted.

每个 JavaFX 11 jar 都有一个 module-info.class 文件,因此根据定义,这些应该被添加到模块路径中。

但如果您不通过 Application 类运行,则不会完成该检查。

主类

Different behaviour between Maven & Eclipse to launch a JavaFX 11 app 的另一个答案解释了当您将 Launcher 类(不扩展 Application 的主类)与 Maven exec:java 插件一起使用时,为什么它可以在没有模块化系统的情况下工作。

总结:

  • 需要使用 Launcher 来解决上述 sun.launcher.LauncherHelper 问题。
  • 就像 maven 插件在类路径中运行一样,将所有依赖项加载到一个独立的线程中,IntelliJ 在这种情况下也是如此。

如果在运行 Main.main() 时检查命令行:

/path/to/jdk-11.0.2.jdk/Contents/Home/bin/java \
    "-javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=60556:/Applications/IntelliJ IDEA.app/Contents/bin"  \
    -Dfile.encoding=UTF-8  \
    -classpath /path/to/so-question-54756176-master/target/classes:/path/to/.m2/repository/org/openjfx/javafx-base/11.0.2/javafx-base-11.0.2.jar:.../path/to/.m2/repository/org/openjfx/javafx-fxml/11.0.2/javafx-fxml-11.0.2-mac.jar  \
    Main

JavaFX SDK 中的所有 JavaFX jar 都已添加到类路径中,并且您正在运行经典的 java -cp ... Main

缺少 javafx.fxml

这些对 IntelliJ IDEA - Error: JavaFX runtime components are missing, and are required to run this application 的其他回答解释了在模块系统上运行但未将 javafx.fxml 添加到 --add-modules 选项时出现的错误。

Caused by: java.lang.IllegalAccessError: class com.sun.javafx.fxml.FXMLLoaderHelper (in unnamed module @0x5fce9dc5) cannot access class com.sun.javafx.util.Utils (in module javafx.graphics) because module javafx.graphics does not export com.sun.javafx.util to unnamed module @0x5fce9dc5
    at com.sun.javafx.fxml.FXMLLoaderHelper.<clinit>(FXMLLoaderHelper.java:38)
    at javafx.fxml.FXMLLoader.<clinit>(FXMLLoader.java:2056)

你的错误说你正在使用 FXML 但它不能在模块路径中解析,所以它试图通过反射访问但失败了,因为你没有打开 javafx.graphics 到你未命名的模块。

那么现在您会问:我没有首先设置 javafx.graphics!

好吧,你没有,但 IntelliJ 为你做了!

运行 MainApp.main() 时检查命令行:

/path/to/jdk-11.0.2.jdk/Contents/Home/bin/java \
    --add-modules javafx.base,javafx.graphics \
    --add-reads javafx.base=ALL-UNNAMED \
    --add-reads javafx.graphics=ALL-UNNAMED \
    "-javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=60430:/Applications/IntelliJ IDEA.app/Contents/bin" \
    -Dfile.encoding=UTF-8 \
    -classpath /path/to/so-question-54756176-master/target/classes:/path/to/.m2/repository/org/openjfx/javafx-base/11.0.2/javafx-base-11.0.2.jar:.../.m2/repository/org/openjfx/javafx-graphics/11.0.2/javafx-graphics-11.0.2-mac.jar \
    MainApp

可以看到IntelliJ默认添加了javafx.basejavafx.graphics。所以只缺少 javafx.fxml(然后您当然应该添加模块路径)。

正如您所指出的,推荐的解决方案在 docs 中:

在命令行上,使用 --module-path 包含 JavaFX SDK lib 文件夹的路径,并使用 --add-modules 包含 javafx.fxml 在这种情况下(您没有控件)。

IntelliJ VM arguments

或者使用 Maven 插件。在某些时候,您将不得不离开您的 IDE,因此您将需要使用插件来运行该应用程序。

Maven 执行

关于 Maven exec 插件的最后说明,以防您使用它:

此外,推荐的 Maven 解决方案,直到插件 exec:java 为模块化系统修复(好消息是这是 done 正如我们所说),将使用 exec:exec 代替,如本 issue 中所述。 , 因此您可以同时指定两个 vm 参数。

关于java - 了解主类如何影响 JPMS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54756176/

相关文章:

java - 我如何获得用户音乐目录?

JavaFX 应用程序无法使用 native exe bundle 启动

java - TableView 和映射。字符和整数

java - 在 Java 11 上安装 tomcat 7 作为服务 :NB: JAVA_HOME should point to a JDK not a JRE

java - Java线程InterruptExceptioon处理仅在某些情况下阻止了进一步的代码执行

java - spring中如何外部化配置?

java - 静态方法或实例方法中的实际代码

java - 创建标签 JavaFX 时子场景中的白色矩形

java - parallelStream() 使用 JAXB-API 导致 ClassNotFoundException

java - JDK 的本地方法是如何加载的?