【问题标题】:Understanding how the main class affects JPMS了解主类如何影响 JPMS
【发布时间】:2019-07-12 08:41:45
【问题描述】:

如果 Application 类不是 Main 类,我有一个非常基本的 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);
    }

}

例外是:

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) 因为模块 javafx.graphics 不导出 com.sun.javafx.util 到未命名模块 @987654332 @

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

主要问题是:为什么相同的代码分布在两个类中会有不同的行为?在这两种情况下FXMLLoader 使用com.sun.javafx.fxml.FXMLLoaderHelper,而后者又使用com.sun.javafx.util.Utils。所以要么我应该在这两种情况下都得到例外,要么都不例外。有什么区别?

【问题讨论】:

  • 看起来很奇怪。请为此提供一个真实的 MCVE。
  • @flakes 尝试运行代码。可获取演示项目on GitHub
  • 完美就是我想要的。在调查此问题时,确切的包名称和构建工具将很重要。

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


【解决方案1】:

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

应用类

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

总结:

如你所见here:

此错误来自 java.base 模块中的sun.launcher.LauncherHelper (link)。

如果主应用扩展Application 并具有main 方法,LauncherHelper 将检查javafx.graphics 模块是否作为命名模块存在:

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

如果该模块不存在,则中止启动。

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

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

主类

Different behaviour between Maven & Eclipse to launch a JavaFX 11 app 的另一个答案解释了为什么当您使用带有 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(您没有控件)。

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

Maven 执行

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

更重要的是,推荐的 Maven 解决方案,直到插件 exec:java 被固定为模块化系统(好消息是这是 done 我们所说的),将是改用exec:exec,正如issue 中所述,因此您可以指定两个vm 参数。

【讨论】:

  • 哇,感谢您的深入回答!非常感谢所有的细节。它还显示了我对这些库的了解程度。所以在离开 IDE 并分发应用程序时,我需要为 javafx 添加ˋ—add-modulesˋ 选项,尽管我自己并没有使用 JPMS,对吧?
  • 您仍然可以使用启动器类和当前的exec:java 插件,但这种情况很快就会改变,而且不会有其他选择,所以您必须使用模块化系统,不管你喜不喜欢……
猜你喜欢
  • 2021-01-12
  • 1970-01-01
  • 2018-06-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多