VSCode:缺少 JavaFX 运行时组件
VSCode: JavaFX runtime components are missing
我有一个 Maven 项目,我想将其导出到 运行nable .jar 文件中。
在 VSCode 中,项目运行没有问题。导出的项目包括一个 .jar 文件和一个包含所有 javaFX 文件的 libs
文件夹:
Documents/prosjekt/target
├── classes
├── generated-sources
├── generated-test-sources
├── libs
│ ├── apiguardian-api-1.1.0.jar
│ ├── javafx-base-18.0.1-mac-aarch64.jar
│ ├── javafx-base-18.0.1.jar
│ ├── javafx-controls-18.0.1-mac-aarch64.jar
│ ├── javafx-controls-18.0.1.jar
│ ├── javafx-fxml-18.0.1-mac-aarch64.jar
│ ├── javafx-fxml-18.0.1.jar
│ ├── javafx-graphics-18.0.1-mac-aarch64.jar
│ ├── javafx-graphics-18.0.1.jar
│ ├── javafx-media-18.0.1-mac-aarch64.jar
│ ├── javafx-media-18.0.1.jar
│ ├── junit-jupiter-api-5.7.0.jar
│ ├── junit-jupiter-engine-5.7.0.jar
│ ├── junit-platform-commons-1.7.0.jar
│ ├── junit-platform-engine-1.7.0.jar
│ └── opentest4j-1.2.0.jar
├── maven-archiver
├── maven-status
├── surefire-reports
├── prosjekt_boilerplate-1.0-SNAPSHOT.jar
└── test-classes
当我运行以下命令时出现问题:
❯ java -jar /Users/user/Documents/prosjekt/target/prosjekt_boilerplate-1.0-SNAPSHOT.jar
Error: JavaFX runtime components are missing, and are required to run this application
我不明白为什么会出现此错误,因为所有 JavaFX 文件都位于 libs
文件夹中并且可见。
我目前使用 Java 来自 SDKman 的 Temurin 18.0.1。
❯ java -version
openjdk version "18.0.1" 2022-04-19
OpenJDK Runtime Environment Temurin-18.0.1+10 (build 18.0.1+10)
OpenJDK 64-Bit Server VM Temurin-18.0.1+10 (build 18.0.1+10, mixed mode)
Pom.xml
文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>project</groupId>
<artifactId>prosjekt_boilerplate</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>prosjekt_boilerplate</name>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>18.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>18.0.1</version>
</dependency>
<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/libs
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>libs/</classpathPrefix>
<mainClass>
asteroids.AsteroidsApp
</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
编辑
我尝试下载 JavaFX SDK 并运行执行以下命令:
❯ java -jar --module-path /Users/user/Downloads/javafx-sdk-18.0.1/lib \
--add-modules=javafx.controls,javafx.media,javafx.fxml,javafx.graphics \
/Users/user/Documents/prosjekt/target/prosjekt_boilerplate-1.0-SNAPSHOT.jar
成功了!但它仍然没有回答我原来的问题。
虽然出于解释目的这是一个很长的答案,但修复实际上非常简单。
唯一需要做的就是将带有模块的 jar 放在模块路径而不是 class路径上。
回答您的问题以解释您原来的方法失败的原因。
The problem occurs when I run the following command:
java -jar /Users/user/Documents/prosjekt/target/prosjekt_boilerplate-1.0-SNAPSHOT.jar
Error: JavaFX runtime components are missing, and are required to run this application
我不明白为什么会出现此错误,因为所有 JavaFX 文件都位于 libs 文件夹中并且可见。
您收到此错误的原因是您 运行 将所有代码从 class 路径而不是模块路径中删除。
现代 JavaFX 版本未设计或不支持来自 class 路径的 运行。它们应该来自模块路径 运行。
你做了以下事情,这将使程序只在class路径上执行:
- 使用您的应用程序创建了一个 jar。
- 在指定执行的 jar 中创建了一个清单 class。
- 向清单添加了一个库路径。
背景信息
您的应用程序 class 不需要是模块化的,其他(non-JavaFX)库可以从 class 路径而不是模块路径执行当放置在 class 路径上时功能正常,但 JavaFX 库将失败。
解决方案是至少将 JavaFX classes 放在模块路径上。理想情况下,您还可以使您的应用程序模块化(为其定义一个 module-info.java
文件)并在您的应用程序中仅使用 non-automatic 模块化 jar(那些也定义了 module-info.java
的)。但理想的不是必须的(除非你想使用jlink打包你的应用程序),只需要在模块路径上有JavaFX classes。
您不能(当前)通过 jar 清单将其他 jar 或文件夹添加到模块路径,就像您目前可以使用 class 路径一样。有关这方面的信息,请参阅此问题的答案(以及对此答案的评论):
- Java modules and the JAR MANIFEST Class-Path attribute
相反,您需要在命令行中指定您添加的模块的位置(或者依靠由 jlink 创建的 JRE 映像 pre-bundles 所需的模块)。
此外,如果您的应用程序是模块化的,您应该使用 -m
开关来指定主 class 由它所在的模块限定,并且您的应用程序 jar 也应该在模块路径上。如果您的应用程序不是模块化的,那么您仍然可以使用 -jar
说明符并将主要 class 名称编码在 jar 清单中,而不是作为 command-line 开关。
例子
因此,假设您将应用程序作为一个 zip 文件分发,其中包含您的应用程序 jar 和依赖库,位于 sub-folder 名为 lib 中。
对于这些示例,应用程序 jar 是一个普通的薄 jar(它不是一个带有阴影或包含 JavaFX 运行时间代码的胖 jar)。
对于 non-modular 应用程序,这在 eden coding 中进行了讨论。用户解压你的包后,假设主要的 class 名称被编码在清单中,那么应用程序可以是 运行 via:
java -jar myJar.jar --module-path lib --add-modules javafx.controls,javafx.fxml
如果您使用其他 JavaFX 模块,例如 javafx-media,它们不是传递依赖项,那么也包括这些。
如果您的应用程序是模块化的,那么您不需要在 jar 清单中编码额外信息,但您需要在 jar 文件中包含 module-info.java
。执行此操作时,最好将您的 jar 与您正在使用的其余模块放在同一个 lib 目录中,这样您就不需要将它单独添加到模块路径中。完成此操作后,您可以 运行 像这样的应用程序(此示例来自 jenkov tutorial on Java modules):
java --module-path lib -m com.jenkov.mymodule/com.jenkov.mymodule.Main
您可以使用jar 命令设置一个主要class 到运行 模块化jar 文件(在jenkov 教程中也有演示)。然后你可以 运行 该模块化 jar 而无需显式提供主要 class,但指定 jar 文件和模块路径(JavaFX 应用程序仍然需要模块路径):
jar -c --file=lib/com-jenkov-mymodule.jar --main-class=com.jenkov.mymodule.Main -C out/com.jenkov.mymodule .
java --module-path lib -jar lib/com-jenkov-javafx.jar
我不知道 jar 命令究竟做了什么将执行命令添加到 jar 文件中,但我不认为它使用清单来执行此操作。目前,我认为 maven jar 命令无法正确执行此操作(我认为它只是将信息放入清单中,而不是将模块化主要 class 执行信息放入 jar)。
总而言之,使用 Java 模块和使用 class 路径是不同且令人困惑的。 . . :-)
破解
有(至少 Java 17)一个 hack 来创建一个单独的启动器 class 绕过 运行 时间检查以允许启动 JavaFX 框架来自模块路径而不是 class 路径,但它不受支持。我不推荐,这里就不多说了。
进一步的建议
因为命令行开关容易出错或打错,所以bst 在部署 JavaFX 应用程序时提供一个 platform-specific 执行脚本(unix/mac shell 脚本或 windows 批处理文件),用户可以更轻松地使用它来执行应用程序。
还可以考虑使用 Java 运行 时间分发,通过压缩的 jlink 图像或 jpackage 创建的安装程序。这让您的用户更轻松,因为他们不需要找到并安装适当的 java 运行 时间来使用您的应用程序。作为奖励,jlink 可以创建示例 shell 脚本来执行您的应用程序。通过 Maven 执行此操作很容易,使用 maven-javafx-plugin 的 jlink 选项并指定 jlinkZipName.
jpackage 可以创建一个 OS 级别的图标,用户只需点击即可进入 运行 应用程序,但不能直接从 maven-javafx-plugin.
其他资源
更多信息,包括包装选项的链接,例如 JPackageScriptFX in the javafx tag info packaging resources。
相关问题:How can I create an all-in-one jar for a JavaFX project with the Maven Assembly plugin?
我有一个 Maven 项目,我想将其导出到 运行nable .jar 文件中。
在 VSCode 中,项目运行没有问题。导出的项目包括一个 .jar 文件和一个包含所有 javaFX 文件的 libs
文件夹:
Documents/prosjekt/target
├── classes
├── generated-sources
├── generated-test-sources
├── libs
│ ├── apiguardian-api-1.1.0.jar
│ ├── javafx-base-18.0.1-mac-aarch64.jar
│ ├── javafx-base-18.0.1.jar
│ ├── javafx-controls-18.0.1-mac-aarch64.jar
│ ├── javafx-controls-18.0.1.jar
│ ├── javafx-fxml-18.0.1-mac-aarch64.jar
│ ├── javafx-fxml-18.0.1.jar
│ ├── javafx-graphics-18.0.1-mac-aarch64.jar
│ ├── javafx-graphics-18.0.1.jar
│ ├── javafx-media-18.0.1-mac-aarch64.jar
│ ├── javafx-media-18.0.1.jar
│ ├── junit-jupiter-api-5.7.0.jar
│ ├── junit-jupiter-engine-5.7.0.jar
│ ├── junit-platform-commons-1.7.0.jar
│ ├── junit-platform-engine-1.7.0.jar
│ └── opentest4j-1.2.0.jar
├── maven-archiver
├── maven-status
├── surefire-reports
├── prosjekt_boilerplate-1.0-SNAPSHOT.jar
└── test-classes
当我运行以下命令时出现问题:
❯ java -jar /Users/user/Documents/prosjekt/target/prosjekt_boilerplate-1.0-SNAPSHOT.jar
Error: JavaFX runtime components are missing, and are required to run this application
我不明白为什么会出现此错误,因为所有 JavaFX 文件都位于 libs
文件夹中并且可见。
我目前使用 Java 来自 SDKman 的 Temurin 18.0.1。
❯ java -version
openjdk version "18.0.1" 2022-04-19
OpenJDK Runtime Environment Temurin-18.0.1+10 (build 18.0.1+10)
OpenJDK 64-Bit Server VM Temurin-18.0.1+10 (build 18.0.1+10, mixed mode)
Pom.xml
文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>project</groupId>
<artifactId>prosjekt_boilerplate</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>prosjekt_boilerplate</name>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>18.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>18.0.1</version>
</dependency>
<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/libs
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>libs/</classpathPrefix>
<mainClass>
asteroids.AsteroidsApp
</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
编辑
我尝试下载 JavaFX SDK 并运行执行以下命令:
❯ java -jar --module-path /Users/user/Downloads/javafx-sdk-18.0.1/lib \
--add-modules=javafx.controls,javafx.media,javafx.fxml,javafx.graphics \
/Users/user/Documents/prosjekt/target/prosjekt_boilerplate-1.0-SNAPSHOT.jar
成功了!但它仍然没有回答我原来的问题。
虽然出于解释目的这是一个很长的答案,但修复实际上非常简单。
唯一需要做的就是将带有模块的 jar 放在模块路径而不是 class路径上。
回答您的问题以解释您原来的方法失败的原因。
The problem occurs when I run the following command:
java -jar /Users/user/Documents/prosjekt/target/prosjekt_boilerplate-1.0-SNAPSHOT.jar
Error: JavaFX runtime components are missing, and are required to run this application
我不明白为什么会出现此错误,因为所有 JavaFX 文件都位于 libs 文件夹中并且可见。
您收到此错误的原因是您 运行 将所有代码从 class 路径而不是模块路径中删除。
现代 JavaFX 版本未设计或不支持来自 class 路径的 运行。它们应该来自模块路径 运行。
你做了以下事情,这将使程序只在class路径上执行:
- 使用您的应用程序创建了一个 jar。
- 在指定执行的 jar 中创建了一个清单 class。
- 向清单添加了一个库路径。
背景信息
您的应用程序 class 不需要是模块化的,其他(non-JavaFX)库可以从 class 路径而不是模块路径执行当放置在 class 路径上时功能正常,但 JavaFX 库将失败。
解决方案是至少将 JavaFX classes 放在模块路径上。理想情况下,您还可以使您的应用程序模块化(为其定义一个 module-info.java
文件)并在您的应用程序中仅使用 non-automatic 模块化 jar(那些也定义了 module-info.java
的)。但理想的不是必须的(除非你想使用jlink打包你的应用程序),只需要在模块路径上有JavaFX classes。
您不能(当前)通过 jar 清单将其他 jar 或文件夹添加到模块路径,就像您目前可以使用 class 路径一样。有关这方面的信息,请参阅此问题的答案(以及对此答案的评论):
- Java modules and the JAR MANIFEST Class-Path attribute
相反,您需要在命令行中指定您添加的模块的位置(或者依靠由 jlink 创建的 JRE 映像 pre-bundles 所需的模块)。
此外,如果您的应用程序是模块化的,您应该使用 -m
开关来指定主 class 由它所在的模块限定,并且您的应用程序 jar 也应该在模块路径上。如果您的应用程序不是模块化的,那么您仍然可以使用 -jar
说明符并将主要 class 名称编码在 jar 清单中,而不是作为 command-line 开关。
例子
因此,假设您将应用程序作为一个 zip 文件分发,其中包含您的应用程序 jar 和依赖库,位于 sub-folder 名为 lib 中。
对于这些示例,应用程序 jar 是一个普通的薄 jar(它不是一个带有阴影或包含 JavaFX 运行时间代码的胖 jar)。
对于 non-modular 应用程序,这在 eden coding 中进行了讨论。用户解压你的包后,假设主要的 class 名称被编码在清单中,那么应用程序可以是 运行 via:
java -jar myJar.jar --module-path lib --add-modules javafx.controls,javafx.fxml
如果您使用其他 JavaFX 模块,例如 javafx-media,它们不是传递依赖项,那么也包括这些。
如果您的应用程序是模块化的,那么您不需要在 jar 清单中编码额外信息,但您需要在 jar 文件中包含 module-info.java
。执行此操作时,最好将您的 jar 与您正在使用的其余模块放在同一个 lib 目录中,这样您就不需要将它单独添加到模块路径中。完成此操作后,您可以 运行 像这样的应用程序(此示例来自 jenkov tutorial on Java modules):
java --module-path lib -m com.jenkov.mymodule/com.jenkov.mymodule.Main
您可以使用jar 命令设置一个主要class 到运行 模块化jar 文件(在jenkov 教程中也有演示)。然后你可以 运行 该模块化 jar 而无需显式提供主要 class,但指定 jar 文件和模块路径(JavaFX 应用程序仍然需要模块路径):
jar -c --file=lib/com-jenkov-mymodule.jar --main-class=com.jenkov.mymodule.Main -C out/com.jenkov.mymodule .
java --module-path lib -jar lib/com-jenkov-javafx.jar
我不知道 jar 命令究竟做了什么将执行命令添加到 jar 文件中,但我不认为它使用清单来执行此操作。目前,我认为 maven jar 命令无法正确执行此操作(我认为它只是将信息放入清单中,而不是将模块化主要 class 执行信息放入 jar)。
总而言之,使用 Java 模块和使用 class 路径是不同且令人困惑的。 . . :-)
破解
有(至少 Java 17)一个 hack 来创建一个单独的启动器 class 绕过 运行 时间检查以允许启动 JavaFX 框架来自模块路径而不是 class 路径,但它不受支持。我不推荐,这里就不多说了。
进一步的建议
因为命令行开关容易出错或打错,所以bst 在部署 JavaFX 应用程序时提供一个 platform-specific 执行脚本(unix/mac shell 脚本或 windows 批处理文件),用户可以更轻松地使用它来执行应用程序。
还可以考虑使用 Java 运行 时间分发,通过压缩的 jlink 图像或 jpackage 创建的安装程序。这让您的用户更轻松,因为他们不需要找到并安装适当的 java 运行 时间来使用您的应用程序。作为奖励,jlink 可以创建示例 shell 脚本来执行您的应用程序。通过 Maven 执行此操作很容易,使用 maven-javafx-plugin 的 jlink 选项并指定 jlinkZipName.
jpackage 可以创建一个 OS 级别的图标,用户只需点击即可进入 运行 应用程序,但不能直接从 maven-javafx-plugin.
其他资源
更多信息,包括包装选项的链接,例如 JPackageScriptFX in the javafx tag info packaging resources。
相关问题:How can I create an all-in-one jar for a JavaFX project with the Maven Assembly plugin?