在AdoptOpenJDK 11与JavaFX 11中带VM参数运行Jar及跨设备适配
我之前用AdoptOpenJDK 11搭配JavaFX 11开发时,也踩过这个JNI错误的坑,正好能给你解决这两个问题:
因为JavaFX 11之后从JDK中分离成了独立模块,必须指定模块路径、依赖模块以及反射权限参数才能正常运行。你可以直接在命令行里把所有VM参数放在java命令后,再跟上-jar和你的Jar文件名:
Windows环境命令
java --module-path "C:\你实际的JavaFX路径\javafx-sdk-11\lib" --add-modules javafx.controls,javafx.fxml,javafx.swing --add-opens javafx.base/com.sun.javafx.runtime=ALL-UNNAMED --add-opens javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED --add-opens javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED --add-opens javafx.base/com.sun.javafx.binding=ALL-UNNAMED --add-opens javafx.base/com.sun.javafx.event=ALL-UNNAMED --add-opens javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED --add-opens javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED -jar YourApp.jar
Linux/macOS环境命令
java --module-path "/你实际的JavaFX路径/javafx-sdk-11/lib" --add-modules javafx.controls,javafx.fxml,javafx.swing --add-opens javafx.base/com.sun.javafx.runtime=ALL-UNNAMED --add-opens javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED --add-opens javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED --add-opens javafx.base/com.sun.javafx.binding=ALL-UNNAMED --add-opens javafx.base/com.sun.javafx.event=ALL-UNNAMED --add-opens javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED --add-opens javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED -jar YourApp.jar
注意:把命令里的C:\你实际的JavaFX路径或/你实际的JavaFX路径替换成你本地JavaFX SDK的lib文件夹绝对路径,YourApp.jar替换成你的Jar文件名。
如果要让其他用户不用输复杂的命令就能运行,有两种常用方案:
方案1:生成运行脚本(简单快速)
把运行命令封装成脚本,用户双击就能执行,还可以把JavaFX依赖和Jar放在同一目录下,用相对路径适配不同环境:
Windows批处理脚本(run_app.bat)
@echo off :: 假设JavaFX的lib文件夹和Jar在同一目录下 set PATH_TO_FX=.\javafx-sdk-11\lib java --module-path "%PATH_TO_FX%" --add-modules javafx.controls,javafx.fxml,javafx.swing --add-opens javafx.base/com.sun.javafx.runtime=ALL-UNNAMED --add-opens javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED --add-opens javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED --add-opens javafx.base/com.sun.javafx.binding=ALL-UNNAMED --add-opens javafx.base/com.sun.javafx.event=ALL-UNNAMED --add-opens javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED --add-opens javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED -jar YourApp.jar pause
Linux/macOS shell脚本(run_app.sh)
#!/bin/bash # 假设JavaFX的lib文件夹和Jar在同一目录下 PATH_TO_FX="./javafx-sdk-11/lib" java --module-path "$PATH_TO_FX" --add-modules javafx.controls,javafx.fxml,javafx.swing --add-opens javafx.base/com.sun.javafx.runtime=ALL-UNNAMED --add-opens javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED --add-opens javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED --add-opens javafx.base/com.sun.javafx.binding=ALL-UNNAMED --add-opens javafx.base/com.sun.javafx.event=ALL-UNNAMED --add-opens javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED --add-opens javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED -jar YourApp.jar
分发时,把你的Jar文件、JavaFX的lib文件夹、脚本放在同一压缩包内,用户解压后双击对应脚本即可运行。
方案2:用jlink生成自定义运行时镜像(专业分发)
jlink可以把你的应用、JavaFX模块、以及所需的JDK部分打包成一个独立的运行时镜像,用户不需要安装JDK/JRE就能直接运行,这是最适合跨平台分发的方案:
步骤1:确保你的应用是模块化的
在项目根目录下创建module-info.java,示例如下:
module your.app.module { requires javafx.controls; requires javafx.fxml; requires javafx.swing; // 替换成你的主类所在包,允许JavaFX反射访问 opens com.yourcompany.yourapp to javafx.fxml; // 导出主类所在包 exports com.yourcompany.yourapp; }
步骤2:执行jlink命令生成镜像
# Windows环境 jlink --module-path "C:\path\to\javafx-sdk-11\lib;C:\path\to\你的项目编译后的classes文件夹" --add-modules your.app.module,javafx.controls,javafx.fxml,javafx.swing --output app_runtime --launcher run=your.app.module/com.yourcompany.yourapp.MainClass # Linux/macOS环境 jlink --module-path "/path/to/javafx-sdk-11/lib:/path/to/你的项目编译后的classes文件夹" --add-modules your.app.module,javafx.controls,javafx.fxml,javafx.swing --output app_runtime --launcher run=your.app.module/com.yourcompany.yourapp.MainClass
步骤3:分发运行镜像
生成的app_runtime文件夹就是完整的运行环境,里面的bin/run.bat(Windows)或bin/run(Linux/macOS)就是启动脚本,用户直接双击即可运行。你可以把这个文件夹打包成zip分发给用户。
如果用Maven/Gradle,可以用对应的JavaFX插件自动化这个过程,比如Maven的javafx-maven-plugin,配置好后执行mvn javafx:jlink就能一键生成镜像。
内容的提问来源于stack exchange,提问作者ValentinDP

