了解主类如何影响JPMS [英] Understanding how the main class affects JPMS
问题描述
我有一个非常基本的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
}
}
但是,当我将两者合并在一起时(这是大多数教程中推荐的方式,包括解决方案
已经发布了一些答案,这些答案可以部分地应用于您的问题,但在此处收集它们并以完整答案显示可能会很方便./p>
应用程序类
在 Maven Shade JavaFX运行时组件丢失的答案中我解释了为什么当您将Application
类用作主类时,应该使用模块系统的原因.
总结:
您可以在此处阅读:
此错误来自java.base模块中的
sun.launcher.LauncherHelper
( Maven和& amp;之间的不同行为Eclipse启动JavaFX 11应用 解释了为什么在Mavenexec:java
插件中使用Launcher
类(主类不扩展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
JavaFX SDK中的所有JavaFX jar都已添加到类路径中,并且您正在运行经典的
java -cp ... Main
.缺少javafx.fxml
docs 中:
在命令行中,使用
--module-path
包括JavaFX SDK lib文件夹的路径,在这种情况下(没有控件),使用--add-modules
包括javafx.fxml
.
或使用Maven插件.在某些时候,您将不得不离开IDE,因此您将需要使用插件来运行该应用程序.
Maven执行者
关于Maven
exec
插件的最后说明,以防您使用它:此外,还是推荐的Maven解决方案,直到为模块化系统固定了插件
exec:java
为止(而且好消息是它正在问题,因此您可以同时指定两个vm参数.I have a very basic JavaFX application that works flawlessly if the Application class is not the Main class:
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 } }
However, when I merge the two together (which is the recommended way in most tutorials, including OpenJFX's official documentation), the module system throws an
IllegalAccessError
(at least on 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); } }
The exception is:
java.lang.IllegalAccessError: class
com.sun.javafx.fxml.FXMLLoaderHelper
(in unnamed module@0x642c1a1b
) cannot access classcom.sun.javafx.util.Utils
(in modulejavafx.graphics
) because modulejavafx.graphics
does not exportcom.sun.javafx.util
to unnamed module@0x642c1a1b
The weird thing is, that I did not actively use the module system. I did not add a
module-info.java
to my project. So I assumed everything should get exported to any unnamed modules? But that is not even the point.The main question is: Why does the same code behave differently if distributed across two classes? In both cases
FXMLLoader
usescom.sun.javafx.fxml.FXMLLoaderHelper
, which in turn usescom.sun.javafx.util.Utils
. So either I should get the exception in both cases or in none. What is the difference?解决方案There are a few answers already posted that could apply partially to your questions, but it might be convenient to collect them here and present them in a full answer.
Application class
In the answer to Maven Shade JavaFX runtime components are missing I explained the reason why, when you use the
Application
class as your main class, you are expected to use the module system.In summary:
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 amain
method, theLauncherHelper
will check for thejavafx.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.
Every JavaFX 11 jar has a
module-info.class
file, so by definition, these are expected to be added to the module path.But if you don't run via
Application
class, that check is not done.Main class
This other answer to Different behaviour between Maven & Eclipse to launch a JavaFX 11 app explains why it works without the modular system when you use a
Launcher
class (a Main class not extending Application) with the Mavenexec:java
plugin.In summary:
- Using a Launcher is required to overcome the mentioned
sun.launcher.LauncherHelper
issue.- Like the maven plugin runs in the classpath, loading all the dependencies into an isolated thread, so does IntelliJ in this case.
If you check the command line when you run
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
All the JavaFX jars from the JavaFX SDK are added to the classpath, and you are running the classic
java -cp ... Main
.javafx.fxml missing
These other answer to IntelliJ IDEA - Error: JavaFX runtime components are missing, and are required to run this application explains the error you get when you run on the module system but you don't add
javafx.fxml
to the--add-modules
option.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)
Your error says that you are using FXML but it can't be resolved in the module-path, so it is trying to access via reflection and that fails since you didn't opened that
javafx.graphics
to your unnamed module.So now you will ask: I didn't set the
javafx.graphics
in the first place!Well, you didn't, but IntelliJ did it for you!
Check the command line when you run
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
You can see that IntelliJ by default adds
javafx.base
andjavafx.graphics
. So only thejavafx.fxml
is missing (and then you should of course add the module-path).The recommended solution, as you pointed out, is in the docs:
Either on command line, using
--module-path
to include the path to your JavaFX SDK lib folder, and--add-modules
to includejavafx.fxml
in this case (where you don't have controls).Or using the Maven plugin. At some point you will have to leave your IDE, so you will need to use a plugin to run the application.
Maven exec
A final note on the Maven
exec
plugin, in case you use it:What's more, the recommended Maven solution, until the plugin
exec:java
is fixed for the modular system (and the good news is that this is being done as we speak), will be usingexec:exec
instead, as explained in this issue, so you can specify both vm arguments.这篇关于了解主类如何影响JPMS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!