Различное поведение Maven и Eclipse при запуске приложения JavaFX 11

Я начинаю копаться в миграции Java 11 для большого приложения (включая части Java FX), и мне нужна ваша помощь, чтобы понять разницу между Maven (3.5.4) в командной строке и Eclipse (2018-09 с обновлением Java11 ).

У меня простой класс Java 11

import java.util.stream.Stream;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;

public class HelloFX extends Application {

    @Override
    public void start(Stage stage) {
        String javaVersion = System.getProperty("java.version");
        String javafxVersion = System.getProperty("javafx.version");
        Label l = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".");
        Scene scene = new Scene(l, 640, 480);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        Stream.of("jdk.module.path",
                "jdk.module.upgrade.path",
                "jdk.module.main",
                "jdk.module.main.class").forEach(key -> System.out.println(key + " : " + System.getProperty(key)));

        Application.launch();
    }

}

и простой помпон

<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.gluonhq</groupId>
    <artifactId>hellofx</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>11</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>HelloFX</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Когда я запускаю mvn compile exec: java, я думаю, что ничто не использует новый путь к модулю, и программа отображает панель JavaFX, как и ожидалось. Система выходит:

jdk.module.path: нуль

jdk.module.upgrade.path: нуль

jdk.module.main: нуль

jdk.module.main.class: нуль

При запуске из средства запуска Eclipse мне нужно добавить в средство запуска следующие аргументы vm:

--module-path = $ {env_var: JAVAFX_PATH} --add-modules = javafx.controls

и панель также отображается, но вывод:

jdk.module.path: C: \ dev \ tools \ javafx-sdk-11 \ lib

jdk.module.upgrade.path: нуль

jdk.module.main: нуль

jdk.module.main.class: нуль

jdk.module.main.class: нуль

Я не могу заставить его работать в Eclipse, так как он работает из командной строки: я вынужден возиться с модулями и путем к модулю. Если я не добавлю параметры vm, я получу либо ошибку: компоненты среды выполнения JavaFX отсутствуют и необходимы для запуска этого приложения, либо ошибка произошла во время инициализации загрузочного уровня java.lang.module.FindException: модуль javafx.controls не найден.

Как он может работать из командной строки без дополнительной настройки? Насколько мне известно, Maven ничего не добавляет автоматически к пути к модулю ...

Любая идея ? Что мне не хватает?

Update1: я понял, что при импорте проекта в Eclipse как проект Maven (что я всегда делаю) это приводит к добавлению JRE в путь к модулю (что не относится к моим проектам classis). См. Снимок экрана  введите описание изображения здесь


person Francois Marot    schedule 10.10.2018    source источник


Ответы (1)


При запуске из командной строки, если вы выбираете систему сборки Maven (то же самое работает для Gradle), вы позволяете плагинам делать работу за вас.

Когда вы запускаете основной класс из своей среды IDE, но не из встроенных окон Maven / Gradle, наоборот, вы запускаете простые java параметры командной строки.

И это приводит к двум разным результатам (но, конечно, с одним и тем же конечным результатом), как вы уже поняли из распечатки свойств.

Как уже упоминалось в этом ответе для IntelliJ, любая другая IDE или эта другая одна для Eclipse существует два способа запуска проекта JavaFX 11 в зависимости от использования или отсутствия системы сборки Maven / Gradle.

Проект JavaFX без инструментов сборки

Чтобы запустить проект JavaFX из среды IDE, необходимо загрузить SDK JavaFX и добавить библиотеку с различные javafx jar-файлы в вашу среду IDE с путем, подобным /Users/<user>/Downloads/javafx-sdk-11/lib/.

Теперь, чтобы запустить этот проект, даже если он не является модульным, вам нужно добавить путь к этим модулям и включить модули, которые вы используете, в параметры / аргументы виртуальной машины проекта.

Независимо от того, запускаете ли вы проект из своей среды IDE или из командной строки, вы будете запускать что-то вроде:

java --module-path /Users/<user>/Downloads/javafx-sdk-11/lib/ \
    --add-modules=javafx.controls org.openjfx.hellofx.HelloFX

Обратите внимание, что даже если ваш проект не является модульным, вы все равно используете модули JavaFX, и, поскольку вы не используете какой-либо инструмент сборки, вы должны в первую очередь позаботиться о загрузке SDK.

Проект JavaFX, инструменты сборки

Если вы используете инструменты сборки Maven или Gradle, первое основное отличие состоит в том, что вам не нужно загружать JavaFX SDK. Вы включите в свой pom (или файл build.gradle), какие модули вам нужны, и Maven / Gradle сможет загрузить только эти модули (и зависимости) в ваш локальный репозиторий .m2 / .gradle.

Когда вы запускаете свой основной класс из Maven exec:java target, вы используете плагин, и то же самое касается задачи run в Gradle.

На данный момент это выглядит при запуске:

mvn compile exec:java

or

gradle run

вы не добавляете приведенные выше аргументы виртуальной машины, но дело в том, что Maven / Gradle позаботятся об этом за вас.

Gradle

В случае с Gradle это более очевидно, поскольку вы должны установить их в задаче run:

run {
    doFirst {
        jvmArgs = [
            '--module-path', classpath.asPath,
            '--add-modules', 'javafx.controls'
        ]
    }
}

Хотя вам не нужен SDK, classpath содержит путь к вашему репозиторию .m2 или .gradle, куда были загружены артефакты javafx.

Maven

Для Maven, в то время как pom управляет зависимостями различных модулей javafx и устанавливает классификатор для загрузки модулей, специфичных для платформы (см., Например, /Users/<User>/.m2/repository/org/openjfx/javafx-controls/11/javafx.controls-11.pom), плагину удается настроить путь к классам и создать необходимые параметры для запуска проекта.

Короче говоря, для вызова вашего класса приложения используется новый класс, который не расширяет Application: HelloFX.main(args).

ИЗМЕНИТЬ

См. Этот ответ для более подробного объяснения того, почему не удается запустить приложение JavaFX без пути к модулю. . Но вкратце:

Эта ошибка исходит от sun.launcher.LauncherHelper в модуле java.base. Причина этого в том, что приложение Main расширяет Application и имеет основной метод. В этом случае LauncherHelper проверит, присутствует ли модуль javafx.graphics как именованный модуль. Если этого модуля нет, запуск прерывается.

Более подробное объяснение того, как плагин maven работает без установки module-path:

Если вы добавите уровень отладки (по умолчанию это информация) при выполнении целей Maven, вы получите более подробную информацию о том, что происходит за кулисами.

Запуск mvn compile exec:java показывает:

 ...
[DEBUG]   (f) mainClass = org.openjfx.hellofx.HelloFX
 ...
[DEBUG] Invoking : org.openjfx.hellofx.HelloFX.main()
 ...

И если вы проверите исходный код exec-maven-plugin, вы можете найдите в ExecJavaMojo::execute, как main метод класса Application вызывается из потока.

Именно это позволяет запускать класс Application из внешнего класса, который не расширяет класс Application, чтобы пропустить проверки.

Заключение

Выбирать инструменты сборки или нет - решать вам, хотя в настоящее время их использование, конечно, является предпочтительным вариантом. В любом случае конечный результат будет одинаковым.

Но важно понимать, в чем различия этих подходов и как ваша IDE справляется с ними.

person José Pereda    schedule 10.10.2018
comment
Спасибо за развернутый ответ, Хосе. тем не менее, я все еще немного потерялся. Особенно, когда вы говорите, что [инструменты сборки] позаботятся об этом за вас. Это кажется неправдой, особенно с тех пор, как я обнаружил, что использую плагин exec-maven с 2011 года (не специально)! Эта версия 1.2.1 ничего не знает о модуле-пути или каких-либо новых материалах Java 11. При этом это не мешает запускать JavaFX 11. В чем фокус ?! Почему в среде IDE требуется конкретная конфигурация с указанием пути к модулю, а в командной строке - нет? - person Francois Marot; 11.10.2018
comment
Я объяснил, как это работает в части Maven: a new class that doesn't extend Application is used to call your application class: HelloFX.main(args). Если у вас есть правильные зависимости в вашем пути к классам, вы всегда можете запустить этот класс без флага module-path / add-modules и запустить свой проект JavaFX. - person José Pereda; 11.10.2018
comment
См. Этот вопрос для объяснения того, как можно использовать этот класс запуска. для создания, например, сверх / толстой банки. Это тот же механизм, который Maven использует с плагином. - person José Pereda; 11.10.2018
comment
Мне очень жаль, Хосе, но я все еще упускаю суть: как показывает код, в HelloFX (расширяющем приложении) у меня есть классический метод main. будь то Maven или eclipse, они оба вызывают этот метод, как всегда в Java. Какой волшебный трюк заставит Eclipse или Maven автоматически определять JavaFX и заставлять их делать что-то другое? - person Francois Marot; 11.10.2018
comment
(для записи, в eclipse я импортирую проект как проект Maven, поэтому m2e должен создать путь к классам точно так же, как Maven в командной строке, но кажется, что JRE добавляется как путь к модулю - см. снимок экрана, который я добавлю в мой исходный пост) (опять же, извините за то, что так настаиваю, но мне нужно в деталях понять весь механизм, чтобы объяснить его команде;) - person Francois Marot; 11.10.2018
comment
Я отредактировал свой ответ, указав более подробную информацию о том, как работает плагин без использования пути к модулю. - person José Pereda; 11.10.2018
comment
СПАСИБО ! Я немного медлителен, но мне нужна была эта техническая деталь;) Спасибо за ваше время. Последний вопрос, если у вас есть время: знаете ли вы, ПОЧЕМУ LauncherHelper выполняет эту проверку, хотя кажется, что приложения JavaFX могут работать (см. Случай Maven), даже если мы не используем путь к модулю? Я имею в виду, есть ли реальная причина, по которой вы заставляете пользователей использовать module-path, в то время как он будет работать отлично, даже если вы его не использовали? - person Francois Marot; 11.10.2018
comment
Это другой вопрос ... но короче говоря, поскольку в Java 9 существует FXHelper class, который проходит путь com.sun.javafx.application.LauncherImpl, а не через main. - person José Pereda; 11.10.2018
comment
Я нашел это: mail-archive.com/openjfx- [email protected]/msg13956.html Мне кажется, что некоторые вещи могут не работать, если не использовать модули. Тем не менее это странно - person Francois Marot; 11.10.2018
comment
Да, я в курсе, это был ответ на сообщение, на которое я указал в одном из упомянутых ответов. Если хотите, можете следить за этой связанной проблемой. - person José Pereda; 11.10.2018
comment
Позвольте нам продолжить это обсуждение в чате. - person Francois Marot; 12.10.2018
comment
@ JoséPereda Я прочитал совет по использованию дополнительного класса с основным методом, который теперь несколько раз вызывает приложение, расширяющее класс. Но только в сочетании с запуском программы через команду maven mvn compile exec: java. Как я могу запустить программу внутри eclipse с зеленым значком запуска? Или запустить его в режиме отладки eclipse? НЕ СНАРУЖИ В КОНСОЛИ В ECLIPSE? - person Mattizin; 15.10.2018
comment
Пожалуйста, разместите новый вопрос - person José Pereda; 15.10.2018