了解主类如何影响JPMS [英] Understanding how the main class affects JPMS

查看:102
本文介绍了了解主类如何影响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应用 解释了为什么在Maven exec: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为止(而且好消息是它正在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 class com.sun.javafx.util.Utils (in module javafx.graphics) because module javafx.graphics does not export com.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 uses com.sun.javafx.fxml.FXMLLoaderHelper, which in turn uses com.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 a main method, the LauncherHelper will check for the javafx.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 Maven exec: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 and javafx.graphics. So only the javafx.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 include javafx.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 using exec:exec instead, as explained in this issue, so you can specify both vm arguments.

这篇关于了解主类如何影响JPMS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆