java - 了解主类如何影响 JPMS
问题描述
如果 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 的官方文档),模块系统会抛出一个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:类
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
。所以要么我应该在这两种情况下都得到例外,要么都不例外。有什么区别?
解决方案
已经发布了一些可能部分适用于您的问题的答案,但在此处收集它们并以完整答案的形式呈现它们可能会很方便。
应用类
在对Maven Shade JavaFX runtime components are missing的回答中,我解释了为什么当您使用Application
该类作为主类时,您应该使用模块系统。
总之:
你可以在这里阅读:
此错误来自
sun.launcher.LauncherHelper
java.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");
}
如果该模块不存在,则中止启动。
每个 JavaFX 11 jar 都有一个module-info.class
文件,因此根据定义,这些文件应该被添加到模块路径中。
但是,如果您不通过Application
类运行,则该检查不会完成。
主班
This other answer to different behavior between Maven & Eclipse to launch a JavaFX 11 app
解释了为什么当您使用带有Maven插件的Launcher
类(一个不扩展应用程序的主类)时它在没有模块化系统的情况下工作。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的这些其他答案- 错误:缺少 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)
您的错误表明您正在使用 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
缺少(然后你当然应该添加模块路径)。
正如您所指出的,推荐的解决方案在文档中:
在命令行上,--module-path
用于包含 JavaFX SDK lib 文件夹的路径,并在这种情况下--add-modules
包含javafx.fxml
(您没有控件)。
或者使用 Maven 插件。在某些时候,您将不得不离开您的 IDE,因此您需要使用插件来运行应用程序。
Maven 执行
关于 Mavenexec
插件的最后说明,以防您使用它:
更重要的是,推荐的 Maven 解决方案,直到为模块化系统修复插件(好消息是我们所说exec:java
的正在完成 ),将改为使用,如本期所述,因此您可以指定两个 vm论据。exec:exec
推荐阅读
- android - 如何在线性布局中插入tabview?
- c++ - 为什么 GCC 不检查命名空间中模板特化的约束
- selenium - 质量保证 | 为单个多驱动程序配置多个 Remote.server 运行 Mobile 到 Web 并返回
- objective-c - 如何在使用 NSString StringWithFormat 的 UILabel 中插入新行
- xcode - 输入通用约束时 Xcode 崩溃
- c# - ASP.NET Core ADO.net 中的自定义 ConfigurationProvider
- pip - 使用 pip3 在 Centos7 上安装 ansible
- docusignapi - 如何在 DocuSign 中使用刷新令牌
- reactjs - 如何在点击事件处理程序中使用 `useSelector()` 的值?
- python - 如何在 python 中使用 ffmpeg 映射字节(内存文件)?