java - Maven 和 Eclipse 之间启动 JavaFX 11 应用程序的不同行为
问题描述
我开始深入研究大型应用程序(包括 Java FX 部分)的 Java 11 迁移,我需要您的帮助来了解命令行上的 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();
}
}
和一个简单的 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:空
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不会自动向模块路径添加任何东西......
任何想法 ?我错过了什么?
更新 1:我意识到当在 Eclipse 中“作为 Maven 项目”导入项目时(我总是这样做),它会导致 JRE 被添加到模块路径中(这不是我的 classis 项目的情况)。看截图
解决方案
从命令行运行时,如果您选择 Maven(Gradle 也适用)构建系统,您可以让插件为您完成工作。
相反,当您从 IDE 运行主类而不是从内置的 Maven/Gradle 窗口时,您正在运行普通的java
命令行选项。
正如您已经通过属性打印出来的那样,这些结果导致了两种不同的事情(但当然最终结果相同)。
正如 IntelliJ 的这个答案已经涵盖的那样,但适用于任何其他 IDE,或者适用于 Eclipse 的另一个IDE,有两种运行 JavaFX 11 项目的方法,具体取决于是否使用 Maven/Gradle 构建系统。
JavaFX 项目,没有构建工具
要从 IDE 运行 JavaFX 项目,您必须下载 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 存储库。
当您从 Mavenexec:java
目标运行主类时,您使用的是插件,run
Gradle 上的任务也是如此。
此时,运行时看起来像:
mvn compile exec:java
或者
gradle run
您没有添加上述 VM 参数,但事实是 Maven/Gradle 正在为您处理它。
摇篮
在 Gradle 案例中,这一点更为明显,因为您必须在run
任务中设置它们:
run {
doFirst {
jvmArgs = [
'--module-path', classpath.asPath,
'--add-modules', 'javafx.controls'
]
}
}
虽然您不需要 SDK,但它classpath
包含下载 javafx 工件的 .m2 或 .gradle 存储库的路径。
马文
对于 Maven,虽然 pom 管理不同 javafx 模块的依赖关系,并设置分类器以下载特定于平台的模块(参见示例/Users/<User>/.m2/repository/org/openjfx/javafx-controls/11/javafx.controls-11.pom
),但插件管理配置类路径并创建运行项目所需的选项。
简而言之,一个不扩展的新类Application
用于调用您的应用程序类:HelloFX.main(args)
.
编辑
有关为什么在没有模块路径的情况下启动 JavaFX 应用程序失败的更详细说明,请参阅此答案。但简而言之:
此错误来自 java.base 模块中的 sun.launcher.LauncherHelper。原因是主应用程序扩展了应用程序并有一个主方法。如果是这种情况,LauncherHelper 将检查 javafx.graphics 模块是否作为命名模块存在。如果该模块不存在,则中止启动。
关于 maven 插件如何在不设置module-path的情况下工作的更详细说明:
如果在运行 Maven 目标时添加调试级别(默认为 info),您将获得有关幕后发生的事情的更详细信息。
跑步mvn compile exec:java
节目:
...
[DEBUG] (f) mainClass = org.openjfx.hellofx.HelloFX
...
[DEBUG] Invoking : org.openjfx.hellofx.HelloFX.main()
...
如果您查看exec-maven-plugin
源代码,您会发现Application 类的方法是如何从线程中调用的ExecJavaMojo::execute
。main
这正是允许从不扩展 Application 类的外部类启动 Application 类以跳过检查的原因。
结论
由您决定是否选择构建工具,当然现在使用它们是首选。不管怎样,最终的结果都是一样的。
但重要的是要了解这些方法的区别以及您的 IDE 如何处理它们。
推荐阅读
- javascript - 动态添加属性的 postMessage 未定义错误
- spring - Spring Data JPA Projection with select distinct
- xml - XML 编码:混合属性和元素
- c# - 如何将 tappedItem 对象发送到下一页?
- svg - 用于 IE10 响应问题的 Svg 图像过滤器
- python - 使用不同的构造函数创建类的副本
- python-2.7 - 如何使 else 在列表理解的嵌套语句中不返回任何内容
- node.js - Npm 获取权限错误
- angular - ngClass 布尔表达式获取模板解析错误
- perl - 如何在 perl 中打印文本文件的行时保持适当的列空间