首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 程式碼熱更新 JAR:利用 jar 命令與 Maven 目錄結構實現開發期快速部署

     編輯:Java 相關知識

應用場景

開發階段頻繁修改類別,重新打包整個 JAR 耗時。若專案採標準 Maven 結構,可編譯後直接用 jar uf 指令增量更新 JAR 內的 com/ 目錄,秒級完成熱部署。

目錄結構假設


project-root/
├── src/
├── target/          # 編譯輸出 classes
├── test.jar         # 需更新的目標 JAR
└── jarUpdaterConfig.properties

核心更新類別


package com.foo.common.base.utils.development;

import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JarUpdater {
    private static final Logger log = LoggerFactory.getLogger(JarUpdater.class);

    @Test
    public void updateClass() throws IOException, InterruptedException {
        // 1. 讀取設定檔
        Properties p = new Properties();
        p.load(new ClassPathResource("jarUpdaterConfig.properties").getInputStream());

        // 2. 編譯最新類別(略,假設已由 IDE/建構工具產出至 target/)
        new ClassUpdater().applySettings(p).compileAndCopyClass();

        String workDir = p.getProperty("workingDirectory");
        String jarName = p.getProperty("jar4UpdateName");
        File targetJar = new File(workDir, jarName);

        if (!targetJar.isFile()) {
            log.error("JAR 不存在: {}", jarName);
            return;
        }

        long oldSize = targetJar.length();
        log.info("準備更新 JAR: {}, 目前大小: {}", jarName, oldSize);

        // 3. 執行 jar uf 命令
        // jar uf  -C  <套件根目錄>
        String cmd = String.format("jar uf %s%s -C %s %s",
                workDir, jarName, workDir + "target", "com");
        log.info("執行指令: {}", cmd);
        Runtime.getRuntime().exec(cmd);

        // 4. 等待檔案修改時間更新(簡易輪詢)
        Date start = new Date();
        while (!FileUtils.isFileNewer(targetJar, start)) {
            log.info("等待 JAR 寫入完成...");
            Thread.sleep(2000);
        }

        // 5. 驗證大小變化
        assert oldSize != targetJar.length() : "JAR 大小未變更,更新可能失敗";
        log.info("JAR 更新完成,新大小: {}", targetJar.length());
    }
}

關鍵點解析

  • jar uf:update file,只替換/新增指定路徑下的類別,不重建整個 JAR。
  • -C target com:切換到 target/ 目錄,將 com/ 遞歸加入 JAR,保持套件結構。
  • 輪詢 FileUtils.isFileNewer:避免 exec 非同步導致後續斷言過早執行。
  • 單元測試斷言 assertNotEquals:確保 JAR 確實被修改。

注意事項

  1. 僅適用於「類別檔案替換」,不支援新增/移除資源檔、修改 Manifest 等結構性變更。
  2. 生產環境嚴禁使用,僅限開發/測試環境加速迭代。
  3. 需確保 JAVA_HOME/bin 在 PATH 中,否則 jar 指令找不到。

進階替代方案

  • JRebel / HotswapAgent:JVM 層面熱插拔,支援方法體修改、新增欄位等更豐富場景。
  • Spring Boot DevTools:自動重啟 + LiveReload,體驗更佳。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved