Понимание того, как основной класс влияет на JPMS - PullRequest
0 голосов
/ 19 февраля 2019

У меня есть очень простое приложение JavaFX, которое работает безупречно, если класс Application not 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
    }

}

Однако, когда я объединяю их вместе (что являетсярекомендуемый способ в большинстве учебных пособий, включая официальную документацию 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.Так что либо я должен получить исключение в обоих случаях, либо ни в одном.В чем разница?

1 Ответ

0 голосов
/ 19 февраля 2019

Уже опубликовано несколько ответов, которые могут частично относиться к вашим вопросам, но было бы удобно собрать их здесь и представить в полном ответе.

Класс приложения

В ответе на отсутствуют компоненты среды выполнения JavaFX Maven Shade Я объяснил причину, по которой при использовании класса 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");
}

Если этот модуль отсутствует, запуск отменяется.

Каждый jar JavaFX 11 имеет файл module-info.class, поэтому, по определению, он должен быть добавлен в путь к модулю.

Но если вы не выполните класс Application, эта проверка не будет выполнена.

Основной класс

Этот другой ответ Различное поведение Maven и Eclipse при запуске приложения JavaFX 11 объясняет, почему оно работает без модульной системы, когда вы используете класс 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

Все jar-файлы JavaFX из JavaFX SDK добавляются в classpath, и вы запускаетеклассический 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 для включения пути к вашей папке lib в JavaFX SDK, и --add-modules для включения javafx.fxml в этом случае (если у вас нет элементов управления).

IntelliJ VM arguments

Или с помощью плагина Maven.В какой-то момент вам придется покинуть свою IDE, поэтому вам нужно будет использовать плагин для запуска приложения.

Maven exec

Последнее замечание о Mavenexec плагин, если вы его используете:

Более того, рекомендуемое решение Maven, пока плагин exec:java не будет установлен для модульной системы (и хорошая новость заключается в том, что это done , как мы говорим ), вместо этого будет использовать exec:exec, как описано в этом выпуске , поэтому вы можете указать оба аргумента vm.

...