了解主类如何影响JPMS

Seb*_*n S 6 java javafx java-platform-module-system java-11 javafx-11

我有一个非常基本的JavaFX应用程序,如果Application类不是 Main类,它可以完美地工作:

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
    }

}
Run Code Online (Sandbox Code Playgroud)

但是,当我将两者合并在一起时(这是大多数教程中推荐的方式,包括OpenJFX的官方文档),模块系统抛出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);
    }

}
Run Code Online (Sandbox Code Playgroud)

例外是:

java.lang.IllegalAccessError:class com.sun.javafx.fxml.FXMLLoaderHelper(在未命名的模块中@0x642c1a1b)无法访问类com.sun.javafx.util.Utils(在模块中javafx.graphics),因为模块javafx.graphics未导出com.sun.javafx.util到未命名的模块@0x642c1a1b

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

主要问题是:如果分布在两个类中,为什么相同的代码表现不同?在两种情况下FXMLLoader使用com.sun.javafx.fxml.FXMLLoaderHelper,反过来使用com.sun.javafx.util.Utils.所以我要么在两种情况下都要得到例外,要么都没有.有什么不同?

Jos*_*eda 5

已经发布了一些可以部分解决您的问题的答案,但是在这里收集它们并在完整答案中呈现它们可能会很方便.

应用类

Maven Shade的答案中缺少JavaFX运行时组件,我解释了原因,当您使用Application该类作为主类时,您应该使用模块系统.

综上所述:

你可以在这里阅读:

此错误来自sun.launcher.LauncherHelperjava.base模块(链接).

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

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

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

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

但是如果你不通过Application课程运行,那么检查就不会完成.

主要课程

Maven和Eclipse之间用于启动JavaFX 11应用程序的不同行为的 另一个答案解释了当您使用LauncherMaven exec:java插件使用类(Main类不扩展Application)时它没有模块化系统的原因.

综上所述:

  • 需要使用启动器来克服上述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
Run Code Online (Sandbox Code Playgroud)

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

javafx.fxml缺失

IntelliJ IDEA的其他答案- 错误:缺少JavaFX运行时组件,并且运行此应用程序所需的内容解释了在模块系统上运行时获得的错误,但未添加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)
Run Code Online (Sandbox Code Playgroud)

您的错误表明您正在使用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
Run Code Online (Sandbox Code Playgroud)

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

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

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

IntelliJ VM参数

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

Maven高管

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

更重要的是,推荐的Maven解决方案,直到插件exec:java被修复为模块化系统(并且好消息是我们说的这样做 ),将使用,如本期中所解释的那样,因此您可以指定两个vm参数.exec:exec