我有一个非常基本的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.");
}
。因此,我应该在两种情况下都应该获得例外,或者在任何情况下都没有。有什么区别?
答案 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
问题。如果在运行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.base
和javafx.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参数。