2026-03-12

JavaFX 編譯成可執行程式

以前有寫過接手 JavaFX 專案的筆記。這篇則是從 0 建置 JavaFX 後的筆記紀錄。

我這次使用以下面的指令建立 javaFX 專案(windows 的話要把\改成^):

mvn archetype:generate \
    -DarchetypeGroupId=org.openjfx \
    -DarchetypeArtifactId=javafx-archetype-simple \
    -DarchetypeVersion=0.0.6 \
    -DgroupId=com.example \
    -DartifactId=jfxsample \
    -Dversion=0.0.0-SNAPSHOT \
    -Djavafx-version=17.0.14

生出來的專案架構如下:

│  pom.xml
│
└─src
    └─main
        └─java
            │  module-info.java
            │
            └─com
                └─example
                        App.java
                        SystemInfo.java

如果我執行 mvn javafx:run,可以啟動專案。

但是 mvn package的話,執行 java -jar target/jfxsample-0.0.0-SNAPSHOT.jar會出現以下錯誤訊息

Error: Unable to access jarfile target/jfxsample-0.0.0-SNAPSHOT.jar

如果你的 jre 是中文版,錯誤訊息則會是

target/jfxsample-0.0.0-SNAPSHOT.jar 中沒有主要資訊清單屬性

解決方法

其實編譯出來的 target/jfxsample-0.0.0-SNAPSHOT.jar 本身可以執行,不過要加上 module-pathadd-modules,這太過麻煩,我自己也沒試試成功,所以請直接去網路上找。

除此之外,解法有 2 種:

解法 1. 修改編譯方式,使之編譯成可執行的 .jar

首先是 App.java,它內容如下:

package com.example;

// import 省略

public class App extends Application {

	@Override
	public void start(Stage stage) {
		// 中略
	}

	public static void main(String[] args) {
		launch();
	}

}

因為有 public static void main(String[] args) method,所以直覺上會以為只要告知生出來的 jar main class 是 com.example.App 就好。

可是com.example.App 是繼承 javafx.application.Application 的 class,根據 AI (Claude Opus 4.5)的說法, JavaFX 會在啟動時檢查模組系統,發現不是從模組化環境啟動就會報錯。

最簡單的解法是另外建立一個 main class,例如

public class Launcher {
	public static void main(String[] args) {
		App.main(args);
	}
}

然後修改 pom,在 build > plugins 加上以下 plugin


<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<version>3.5.1</version>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>shade</goal>
			</goals>
			<configuration>
				<transformers>
					<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
						<mainClass>com.example.Launcher</mainClass> <!-- 根據 Launcher class 自行調整 -->
					</transformer>
				</transformers>
			</configuration>
		</execution>
	</executions>
</plugin>

第二種方式是修改 javafx plugin,如下:

原本的 javafx plugin 修改後的
<plugin>
	<groupId>org.openjfx</groupId>
	<artifactId>javafx-maven-plugin</artifactId>
	<version>0.0.6</version>
	<executions>
		<execution>
			<id>default-cli</id>
			<configuration>
				<mainClass>com.example.App</mainClass>
			</configuration>
		</execution>
	</executions>
</plugin>
<plugin>
	<groupId>org.openjfx</groupId>
	<artifactId>javafx-maven-plugin</artifactId>
	<version>0.0.6</version>
	<configuration>
		<stripDebug>true</stripDebug>
		<compress>2</compress>
		<noHeaderFiles>true</noHeaderFiles>
		<noManPages>true</noManPages>
		<launcher>jfxsample</launcher>
		<jlinkImageName>jfxsample</jlinkImageName>
		<jlinkZipName>jfxsample</jlinkZipName>
		<mainClass>com.example/com.example.App</mainClass>
	</configuration>
</plugin>

修改後,執行 mvn javafx:jlink 後, 執行 target/jfxsample/bin/jfxsample.bat 即可啟動程式

因為我是 windows,所以才是執行這個檔案,如果是 mac / Linux 要另外在自己的環境編譯。

如果要把整個編譯好的程式給別人的話,給他 target/jfxsample 資料夾即可;javafx:jlink 也有將編譯好的程式壓縮,位於 target/jfxsample.zip

如果要用 jpackage 打包成安裝檔,jpackage 指令如下(以下是編譯成 .msi 安裝檔,根據自己的需求自行調整)

jpackage --type msi \
    --name JfxSample \
    --app-version 1.0.0 \
    --vendor "Example Inc." \
    --runtime-image target/jfxsample \
    --module com.example/com.example.App \
    --dest target/installer

如果要將 jpackage 加入至 pom,可參閱先前的筆記的《jpackage》章節

補充

以下是 javafx:jlink 的各樣參數,來源是javafx-maven-plugin 的 github README,使用 ChatGPT 5.3 翻譯、整理成表格

參數 數值 默認值 說明
stripDebug false / true false 移除除錯資訊(debug information)。
stripJavaDebugAttributes false / true false 移除 Java 除錯屬性(Java 13 之後支援)。
compress 0 / 1 / 2 0 設定資源壓縮等級。
noHeaderFiles false / true false 移除產生的 runtime image 中的 includes 目錄。
noManPages false / true false 移除產生的 runtime image 中的 man 目錄。
bindServices false / true false 加入綁定服務(bind services)選項。
ignoreSigningInformation false / true false 忽略簽章資訊(signing information)。
jlinkVerbose false / true false 啟用 verbose 模式輸出詳細資訊。
launcher 自訂設定

編譯好後的執行檔名稱。

例如<launcher>foo</launcher>,則在 windows 環境下,會編譯好可執行的 .bat 檔,位於 {專案資料夾}/target/{ jlinkImageName 參數的值}/foo.bat

  • 若有 options 會當作 VM options 傳入;
  • 若有 commandLineArgs 會當作命令列參數傳入。

如果沒有值,就不會編譯出執行檔出來。

jlinkImageName 自訂字串 image 產生的 runtime image 資料夾名稱。
jlinkZipName 自訂字串 若有設定就會將產生的 runtime image 打包為 zip,並以 jlinkZipName 的值作為 .zip 檔的名稱。
jlinkExecutable jlink 完整位置或執行檔名稱 PATH 中的 jlink 指定 jlink 執行檔位置(若在 PATH 中可只填名稱)。
jmodsPath 路徑 使用本地 JavaFX SDK 時,指定 JavaFX jmods 的位置。

參考資料

Mihalceanu , A. (2023, November 14). Package a JavaFX Application as a Platform Specific Executable. Inside Java. https://inside.java/2023/11/14/package-javafx-native-exec/

Onhhkj. (2024, June 25). 关于jdk9 的模块化及以上启动javafx 不使用--Add-Modules方式,如何使用引导类启动及其原理简单分析及报错缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序 问题解决. CSDN. https://blog.csdn.net/u010022461/article/details/139926021

Step-by-Step Guide: Build Executable JAR with JavaFX 11 from Maven (Fix NoClassDefFoundError). (2025, November 7). JavaThinking.Com. https://www.javathinking.com/blog/build-executable-jar-with-javafx11-from-maven/

參考用 AI
  • ChatGPT 5.3
  • Claude Opus 4.5

沒有留言:

張貼留言