Leiningen编译的含JavaFX的Uberjar缺失运行时组件问题排查
我有一个使用JavaFX 21.0.7的极简Clojure项目,仅包含一个文件,功能为显示带空面板的窗口,代码如下:
(ns choosechee.cheeboard.program (:gen-class :extends javafx.application.Application) (:import [javafx.application Application] [javafx.stage Stage] [javafx.scene Scene] [javafx.scene.layout Pane])) (defn -main [& args] (Application/launch choosechee.cheeboard.program args)) (defn -start [this primaryStage] (doto primaryStage (.setScene (Scene. (Pane.) 300 250)) (.show)))
项目配置文件project.clj内容:
(defproject choosechee.cheeboard "0.1.0-SNAPSHOT" :description "Placeholder" :url "http://example.com/FIXME" :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" :url "https://www.eclipse.org/legal/epl-2.0/"} :dependencies [[org.clojure/clojure "1.12.0"] [org.openjfx/javafx-base "21.0.7"] [org.openjfx/javafx-graphics "21.0.7"] [org.openjfx/javafx-controls "21.0.7"]] :repl-options {:init-ns choosechee.cheeboard.program} :main choosechee.cheeboard.program :target-path "target/%s" :profiles {:uberjar {:aot :all}})
运行lein uberjar成功生成两个JAR包:choosechee.cheeboard-0.1.0-SNAPSHOT.jar和choosechee.cheeboard-0.1.0-SNAPSHOT-standalone.jar。我认为standalone包应包含JavaFX可独立运行,但执行时出现错误:
Error: JavaFX runtime components are missing, and are required to run this application
JAR包中似乎包含JavaFX类,不清楚缺失的运行时组件是什么,系统为Kubuntu 24.10,请问该如何解决?
问题根源:JavaFX依赖分为纯Java类(已打包进uberjar)和平台相关的本地原生库(如Linux下的
.so文件)两类,原生库未被自动包含进uberjar,但JavaFX运行必须依赖这些组件。具体解决步骤:
添加平台专属JavaFX依赖
JavaFX的Maven包需要指定对应平台的分类器,修改project.clj的:dependencies,替换原JavaFX依赖为Linux版本::dependencies [[org.clojure/clojure "1.12.0"] [org.openjfx/javafx-base "21.0.7" :classifier "linux"] [org.openjfx/javafx-graphics "21.0.7" :classifier "linux"] [org.openjfx/javafx-controls "21.0.7" :classifier "linux"]]若需跨平台,可通过Leiningen的profile区分:
:profiles {:linux {:dependencies [[org.openjfx/javafx-base "21.0.7" :classifier "linux"] [org.openjfx/javafx-graphics "21.0.7" :classifier "linux"] [org.openjfx/javafx-controls "21.0.7" :classifier "linux"]]} :uberjar {:aot :all}}打包时指定profile:
lein with-profile linux uberjar用插件处理原生库打包
Leiningen默认不会将原生库放到uberjar的正确加载位置,添加插件自动处理::plugins [[lein-native-deps "1.0.0"]]该插件会提取依赖中的原生库到uberjar的
native目录,并在运行时自动加载。手动指定模块路径运行
若不想用插件,可手动指定JavaFX模块路径运行。找到本地Maven仓库中JavaFX原生库的所在目录,执行:java --module-path /path/to/javafx/libs --add-modules javafx.controls,javafx.graphics -jar choosechee.cheeboard-0.1.0-SNAPSHOT-standalone.jar用jlink构建完全独立的JRE
若需要无需额外依赖的可执行文件,使用jlink打包JRE、JavaFX模块和项目代码:- 确保安装JDK 21(包含jlink工具)
- 生成编译后的类文件:
lein compile - 执行jlink命令:
jlink --module-path $JAVA_HOME/jmods:/path/to/javafx/jmods:target/classes \ --add-modules choosechee.cheeboard,javafx.controls,javafx.graphics \ --output target/jre \ --launcher app=choosechee.cheeboard/choosechee.cheeboard.program - 之后直接运行
target/jre/bin/app即可启动程序。
内容的提问来源于stack exchange,提问作者Choosechee

