Различное поведение между 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: null
jdk.module.upgrade.path: null
jdk.module.main: null
jdk.module.main.class: null
При запуске из модуля запуска 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: null
jdk.module.main: null
jdk.module.main.class: null
jdk.module.main.class: null
Я не могу заставить его работать в Eclipse, так как он работает из командной строки: я вынужден связываться с модулями и путь к модулю. Если я не добавляю параметры vm, я получаю либо "Ошибка: отсутствуют компоненты времени выполнения JavaFX, которые необходимы для запуска этого приложения", либо "Ошибка произошла во время инициализации загрузочного слоя java.lang.module.FindException: Модуль javafx.controls не найдено".
Как это может работать из командной строки без дополнительной настройки? Насколько мне известно, Maven не добавляет ничего автоматически к пути модуля...
Любая идея? Что мне не хватает?
Обновление 1: я понял, что при импорте проекта в Eclipse "как проект Maven" (что я всегда делаю) это приводит к добавлению JRE в путь к модулю (что не относится к моим проектам classis). Смотрите скриншот
1 ответ
При запуске из командной строки, если вы выбираете систему сборки Maven (то же самое работает для Gradle), вы позволяете плагинам делать всю работу за вас.
Когда вы запускаете из своей среды IDE основной класс, а не из встроенных окон Maven/Gradle, напротив, вы запускаете простой java
параметры командной строки.
И эти результаты в двух разных вещах (но, конечно, с одинаковым конечным результатом), как вы уже выяснили через распечатку свойств.
Как уже упоминалось в этом ответе для IntelliJ, но применяется к любой другой среде IDE или к этому другому для Eclipse, существует два способа запуска проекта JavaFX 11, основанные на использовании или не использовании системы сборки Maven/Gradle.
Проект JavaFX, без инструментов сборки
Чтобы запустить ваш проект JavaFX из вашей IDE, вы должны загрузить JavaFX SDK и добавить библиотеку с различными javafx jar-файлами в вашу IDE с таким путем, как /Users/<user>/Downloads/javafx-sdk-11/lib/
,
Теперь, чтобы запустить этот проект, даже если он не является модульным, вы должны добавить путь к этим модулям и включить модули, которые вы используете, в опции / аргументы VM проекта.
Запускаете ли вы проект из 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
цель, которую вы используете плагин, и то же самое касается run
задание на Gradle.
На данный момент это выглядит, когда вы запускаете:
mvn compile exec:java
или же
gradle run
Вы не добавляете вышеупомянутые аргументы VM, но дело в том, что Maven/Gradle позаботится об этом за вас.
Gradle
В случае Gradle это более очевидно, так как вы должны установить их в run
задача:
run {
doFirst {
jvmArgs = [
'--module-path', classpath.asPath,
'--add-modules', 'javafx.controls'
]
}
}
Пока вам не нужен SDK, classpath
содержит путь к вашему.m2 или.gradle репозиторию, куда были загружены артефакты javafx.
специалист
Для 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 расширяет приложение и имеет метод main. Если это так, LauncherHelper проверит наличие модуля javafx.graphics в качестве именованного модуля. Если этот модуль отсутствует, запуск отменяется.
Более подробное объяснение того, как плагин maven работает без установки пути к модулю:
Если вы добавите уровень отладки (по умолчанию это информация) при выполнении целей 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 справляется с ними.