🌱不改程式也能換設定:兩種方法安全替換 JAR 裡的 cploader.properties(新手也看得懂)

 

你會學到什麼

  • 什麼是 cploader.properties,為什麼常被打包在 JAR 裡

  • 兩種安全做法:外部覆蓋(推薦)直接改 JAR(次選)

  • 如何確認程式真的吃到你換過的設定

  • 最常踩的坑與速解


快速結論(懶人包)

  • 99% 情況請用「外部覆蓋」:把新的 cploader.properties 放到外部資料夾,啟動時把這個資料夾放進 classpath 最前面,就能蓋過 JAR 內的同名檔案。

  • 只有在你非常確定且需要固化變更時,才考慮「直接把檔塞進 JAR」;不過有簽章的 JAR 會失效,風險較高。


背景小白話

Java 應用程式會從「課本清單」(classpath)依序找資源。只要你把外部資料夾排在清單最前面,同名檔案就能蓋過JAR 裡的版本。這就像把新菜單放在一疊紙的最上面,點餐的人會先看到你新的那張。


方法一:外部覆蓋(推薦、安全、可回滾)

1) 準備「外部設定」資料夾

把你的新 cploader.properties 放進一個資料夾(底下示範 Windows 與 Linux/macOS;資料夾名稱可自訂成 configsettings):

  • Windows(CMD/PowerShell)

mkdir config copy /Y cploader.properties .\config\
  • Linux / macOS

mkdir -p config cp cploader.properties ./config/

2) 判斷你的 JAR 類型

(A)Spring Boot「胖 JAR」:JAR 裡面會有 BOOT-INF/ 這個資料夾。
(B)一般 JAR:沒有 BOOT-INF/

你可以先掃描一下:

jar tf app.jar | findstr /I BOOT-INF

有看到 BOOT-INF 就是(A)。

3) 正確啟動方式

(A)Spring Boot 胖 JAR
建議用 PropertiesLauncher 讓外部資料夾先被載入:

  • Windows

java -cp "config;app.jar" org.springframework.boot.loader.PropertiesLauncher
  • Linux / macOS

java -cp "config:app.jar" org.springframework.boot.loader.PropertiesLauncher

小重點:-cp 是 classpath。把 config 放在 app.jar 前面,就是讓外部設定優先級更高。


(B)一般 JAR(非 Spring Boot)

先查 Main-Class 再啟動:

jar xf app.jar META-INF\MANIFEST.MF type META-INF\MANIFEST.MF | findstr /B /C:"Main-Class:"

假設出現 Main-Class: demo.Main,則:

  • Windows

java -cp "config;app.jar" demo.Main
  • Linux / macOS

java -cp "config:app.jar" demo.Main

方法二:把新檔案放進 JAR(次選,有風險)

先備份,且注意:JAR 若帶簽章(META-INF/*.SF*.RSA)會在修改後失效


1) 備份

copy /Y app.jar app.backup.jar

2) 決定正確目錄

  • Spring Boot 胖 JAR:放在 BOOT-INF/classes/cploader.properties

  • 一般 JAR:放在 根目錄 cploader.properties

3) 更新 JAR 內容

Spring Boot 範例:

mkdir _tmp\BOOT-INF\classes copy /Y cploader.properties _tmp\BOOT-INF\classes\ jar uf app.jar -C _tmp BOOT-INF\classes\cploader.properties

一般 JAR 範例:

jar uf app.jar -C . cploader.properties

4) 目視驗證

jar tf app.jar | findstr /I cploader.properties jar xf app.jar BOOT-INF\classes\cploader.properties type BOOT-INF\classes\cploader.properties

(一般 JAR 抽取時改用 jar xf app.jar cploader.properties


如何確認程式真的吃到新的設定?

做法 A:加入「唯一標記」

cploader.properties 末行加一條不影響功能的註記,例如:

verify.marker = LOADED_2025_11_17_1015

啟動後從 log 搜尋 LOADED_2025_11_17_1015 或觀察有無載入該設定。

做法 B:改一個明顯「看得出來」的參數

例如把輸出資料夾從

output.dir = logs/result

改成

output.dir = logs/result_changed

跑一次流程,看看新資料夾是否真的生成。

做法 C:比對檔案雜湊

抽出 JAR 內的檔案,算 SHA-256 與你手上版本比對:

jar xf app.jar BOOT-INF\classes\cploader.properties certutil -hashfile BOOT-INF\classes\cploader.properties SHA256

常見坑位與速解

  • 中文或空白路徑沒加雙引號

    • java -cp "C:\My App\config;app.jar" ...

    • java -cp C:\My App\config;app.jar ...

  • 找不到 javajar 指令

    • 請把 %JAVA_HOME%\bin 加到環境變數 PATH,或用完整路徑 "%JAVA_HOME%\bin\java"

  • java -jar app.jar 卻吃不到外部設定

    • Spring Boot 下請改用 PropertiesLauncher + -cp,否則 loader.path 不會生效。

  • JAR 有簽章,更新後啟動失敗

    • 改走「外部覆蓋」;或重新簽章(多數情境不建議)。

  • 應用程式沒有用 classpath 找檔

    • 有些系統用絕對路徑或 JVM 參數(如 -Dconfig.file)載入。請查看專案文件或 log,必要時用該參數指定你的檔案位置。


一鍵啟動腳本(全新範例,支援自動偵測)

此批次檔會:建立外部 config、複製新檔、偵測是否 Spring Boot、用正確方式啟動。變數與結構與文中其他片段不同,避免隱私重複。


@echo off setlocal rem === 請自行調整這三個檔名 === set "APP_JAR=app.jar" set "PROP_FILE=cploader.properties" set "CFG_DIR=config" if not exist "%APP_JAR%" (echo [ERR] 找不到 %APP_JAR% & exit /b 1) if not exist "%PROP_FILE%" (echo [ERR] 找不到 %PROP_FILE% & exit /b 1) echo [1/3] 準備外部設定... if not exist "%CFG_DIR%" mkdir "%CFG_DIR%" copy /Y "%PROP_FILE%" "%CFG_DIR%\" >nul || (echo [ERR] 複製失敗 & exit /b 1) echo [2/3] 偵測是否 Spring Boot 胖 JAR... jar tf "%APP_JAR%" | findstr /I "BOOT-INF/" >nul if %errorlevel%==0 ( echo → 偵測到 BOOT-INF/,改用 PropertiesLauncher echo [3/3] 啟動中(按 Ctrl+C 可停止)... java -cp "%CFG_DIR%;%APP_JAR%" org.springframework.boot.loader.PropertiesLauncher ) else ( echo → 一般 JAR,讀取 Main-Class jar xf "%APP_JAR%" META-INF\MANIFEST.MF for /f "tokens=1,* delims=:" %%A in ('type META-INF\MANIFEST.MF ^| findstr /B /C:"Main-Class:"') do set "MAIN=%%B" set "MAIN=%MAIN: =%" if not defined MAIN ( echo [WARN] 解析不到 Main-Class,請手動指定,例如: echo java -cp "%CFG_DIR%;%APP_JAR%" your.main.Class ) else ( echo → Main-Class=%MAIN% echo [3/3] 啟動中(按 Ctrl+C 可停止)... java -cp "%CFG_DIR%;%APP_JAR%" %MAIN% ) ) endlocal

FAQ

Q1:一定要用 config 這個資料夾名嗎?
A:不必,任何名字都可以,重點是把它放在 classpath 最前面

Q2:-Dloader.path 可不可以?
A:對 Spring Boot 而言它要搭配 PropertiesLauncher 才有感。最穩的就是本文做法:-cp "config;app.jar" org.springframework.boot.loader.PropertiesLauncher

Q3:如果原本就是用 java -jar app.jar 怎麼辦?
A:把啟動方式改成本文範例(-cp + PropertiesLauncher)。這是最小變更、最大相容的解法。

Q4:怎麼把變更「寫回」JAR?
A:用「方法二」的 jar uf;但要先確認沒有簽章,或你能接受失效風險。否則請維持外部覆蓋即可。


範例檔案命名建議(避免衝突)

  • cploader.properties 內加上版本號或標記,例如:

    app.version = 1.2.3 verify.marker = LOADED_GUIDE_EXAMPLE
  • 外部設定資料夾可命名成 config-prod/config-staging/,方便一鍵切換環境。


結語

若你只是想快速、安全地更新設定,請選擇「外部覆蓋」。它不動到 JAR 本體、好回滾、也最不會踩坑;必要時再考慮把檔案寫回 JAR。照本文步驟,你不需要會 Java,也能把設定穩穩地換成功。

留言

這個網誌中的熱門文章

🔍Vue.js 專案錯誤排查:解決 numericFields is not defined 與合併儲存格邏輯最佳化

🔎EF Core 連 Oracle 出現 ORA-00600 [kpp_concatq:2] 的完整排錯指南(含 EF Core ToString/CultureInfo 錯誤)

🛠【ASP.NET Core + Oracle】解決 ORA-00904 "FALSE": 無效的 ID 錯誤與資料欄位動態插入顯示問題