了解主类如何影响JPMS

时间:2019-02-18 22:15:20

标签: java javafx java-11 jigsaw 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
    }

}

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

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:类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使用if (TableName.TABLENAME.field("name") != null) { System.out.println("The field is there: " + TableName.TABLENAME.field("name").getName()); } else { System.out.println("There is no field with that name."); } 。因此,我应该在两种情况下都应该获得例外,或者在任何情况下都没有。有什么区别?

1 个答案:

答案 0 :(得分:5)

已经发布了一些答案,可以部分地应用于您的问题,但是将它们收集在此处并提供完整的答案可能会很方便。

应用程序类

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

总结:

  

您可以阅读here

     

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

     

如果主应用扩展了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 的另一个回答解释了为什么当您将Launcher类(主类不扩展应用程序)与Maven exec:java插件一起使用时,为什么在没有模块化系统的情况下也能工作。

总结:

  • 需要使用启动器才能解决上述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参数。