Maven 和 Eclipse 之间启动 JavaFX 11 应用程序的不同行为
Different behaviour between Maven & Eclipse to launch a JavaFX 11 app
我开始深入研究 Java 11 大型应用程序的迁移(包括 Java FX 部分),我需要您的帮助以了解 Maven (3.5.4) 在命令行和 Eclipse(2018-09 Java11 升级)。
我有一个简单的Java11class
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();
}
}
和一个简单的pom
<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 运行time components are missing, and are required to 运行 this application”或“Error occurred during the initialization of引导层 java.lang.module.FindException:找不到模块 javafx.controls。
它如何在没有更多配置的情况下从命令行工作?据我所知,Maven 不会自动向模块路径添加任何内容...
有什么想法吗?我错过了什么?
Update1:我意识到当在 Eclipse 中导入项目“作为 Maven 项目”(这是我一直做的)时,它导致在模块路径中添加了 JRE(我的 class是项目)。看截图
当从命令行 运行ning 时,如果您选择 Maven(同样适用于 Gradle)构建系统,您可以让插件为您完成工作。
当你 运行 来自你的 IDE 主 class,而不是来自内置 Maven/Gradle windows,相反,你运行正在使用普通的 java
命令行选项。
这会导致两种不同的结果(当然最终结果相同),正如您已经通过属性打印出来的那样。
正如这个 for IntelliJ, but applies to any other IDE, or this other 的 Eclipse 所涵盖的那样,根据是否使用 Maven/Gradle 构建系统,有两种 运行 构建 JavaFX 11 项目的方法。
JavaFX 项目,没有构建工具
从您的 IDE 到 运行 您的 JavaFX 项目,您必须下载 JavaFX SDK 并将包含不同 javafx jar 的库添加到您的 IDE,路径如 /Users/<user>/Downloads/javafx-sdk-11/lib/
.
现在,对于运行那个项目,即使它不是模块化的,你也必须添加这些模块的路径,并将你正在使用的模块包含到options/arguments的VM中项目。
无论您是 运行 来自 IDE 还是命令行的项目,您都将 运行ning 类似于:
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 存储库.
当你 运行 你的主要 class 来自 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
包含已下载 javafx 工件的 .m2 或 .gradle 存储库的路径。
Maven
对于 Maven,虽然 pom 管理不同 javafx 模块的依赖关系,并设置 classifier 来下载特定于平台的模块(例如参见 [=21=]),插件管理配置 class 路径并为 运行 项目创建所需的选项。
简而言之,一个不扩展Application
的新class用于调用您的应用程序class:HelloFX.main(args)
.
编辑
请参阅此 以获取有关启动 JavaFX 应用程序 没有模块路径 失败的原因的更详细解释。但简而言之:
This error comes from sun.launcher.LauncherHelper in the java.base module. The reason for this is that the Main app extends Application and has a main method. If that is the case, the LauncherHelper will check for the javafx.graphics module to be present as a named module. If that module is not present, the launch is aborted.
关于maven插件如何在不设置模块路径的情况下工作的更详细的解释:
如果在 运行Maven 目标时添加调试级别(默认为信息),您将获得有关幕后发生的事情的更多详细信息。
运行 mvn compile exec:java
显示:
...
[DEBUG] (f) mainClass = org.openjfx.hellofx.HelloFX
...
[DEBUG] Invoking : org.openjfx.hellofx.HelloFX.main()
...
如果您检查 exec-maven-plugin
源代码,您可以 find 在 ExecJavaMojo::execute
如何调用应用程序 class 的 main
方法一个线程。
这正是允许从不扩展应用程序 class 的外部 class 启动应用程序 class 以跳过检查的原因。
结论
是否选择构建工具取决于您,尽管现在使用它们当然是首选。无论哪种方式,最终结果都是一样的。
但重要的是要了解这些方法的区别,以及您的 IDE 如何处理它们。
我开始深入研究 Java 11 大型应用程序的迁移(包括 Java FX 部分),我需要您的帮助以了解 Maven (3.5.4) 在命令行和 Eclipse(2018-09 Java11 升级)。
我有一个简单的Java11class
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();
}
}
和一个简单的pom
<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 运行time components are missing, and are required to 运行 this application”或“Error occurred during the initialization of引导层 java.lang.module.FindException:找不到模块 javafx.controls。
它如何在没有更多配置的情况下从命令行工作?据我所知,Maven 不会自动向模块路径添加任何内容...
有什么想法吗?我错过了什么?
Update1:我意识到当在 Eclipse 中导入项目“作为 Maven 项目”(这是我一直做的)时,它导致在模块路径中添加了 JRE(我的 class是项目)。看截图
当从命令行 运行ning 时,如果您选择 Maven(同样适用于 Gradle)构建系统,您可以让插件为您完成工作。
当你 运行 来自你的 IDE 主 class,而不是来自内置 Maven/Gradle windows,相反,你运行正在使用普通的 java
命令行选项。
这会导致两种不同的结果(当然最终结果相同),正如您已经通过属性打印出来的那样。
正如这个
JavaFX 项目,没有构建工具
从您的 IDE 到 运行 您的 JavaFX 项目,您必须下载 JavaFX SDK 并将包含不同 javafx jar 的库添加到您的 IDE,路径如 /Users/<user>/Downloads/javafx-sdk-11/lib/
.
现在,对于运行那个项目,即使它不是模块化的,你也必须添加这些模块的路径,并将你正在使用的模块包含到options/arguments的VM中项目。
无论您是 运行 来自 IDE 还是命令行的项目,您都将 运行ning 类似于:
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 存储库.
当你 运行 你的主要 class 来自 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
包含已下载 javafx 工件的 .m2 或 .gradle 存储库的路径。
Maven
对于 Maven,虽然 pom 管理不同 javafx 模块的依赖关系,并设置 classifier 来下载特定于平台的模块(例如参见 [=21=]),插件管理配置 class 路径并为 运行 项目创建所需的选项。
简而言之,一个不扩展Application
的新class用于调用您的应用程序class:HelloFX.main(args)
.
编辑
请参阅此
This error comes from sun.launcher.LauncherHelper in the java.base module. The reason for this is that the Main app extends Application and has a main method. If that is the case, the LauncherHelper will check for the javafx.graphics module to be present as a named module. If that module is not present, the launch is aborted.
关于maven插件如何在不设置模块路径的情况下工作的更详细的解释:
如果在 运行Maven 目标时添加调试级别(默认为信息),您将获得有关幕后发生的事情的更多详细信息。
运行 mvn compile exec:java
显示:
...
[DEBUG] (f) mainClass = org.openjfx.hellofx.HelloFX
...
[DEBUG] Invoking : org.openjfx.hellofx.HelloFX.main()
...
如果您检查 exec-maven-plugin
源代码,您可以 find 在 ExecJavaMojo::execute
如何调用应用程序 class 的 main
方法一个线程。
这正是允许从不扩展应用程序 class 的外部 class 启动应用程序 class 以跳过检查的原因。
结论
是否选择构建工具取决于您,尽管现在使用它们当然是首选。无论哪种方式,最终结果都是一样的。
但重要的是要了解这些方法的区别,以及您的 IDE 如何处理它们。