跳至主要内容

主版本升級方法論

框架主版本升級(如 Spring Boot 3.x → 4.x)的標準化流程與最佳實踐。

本方法論基於實際升級經驗持續改進。


為什麼需要特殊的升級策略?

主版本升級通常包含大量 breaking changes

  • API 重新命名或移除
  • 依賴套件變更(如 Jackson 2 → 3 改變了套件名稱)
  • 配置方式改變
  • 預設行為變更

直接升級(在現有專案上修改版本號)會導致:

  1. 大量編譯錯誤同時出現,難以定位
  2. 可能遺漏某些配置變更
  3. 升級後無法啟動時,難以判斷原因
  4. 沿用舊的配置方式,錯過官方建議的新做法

推薦策略:從乾淨框架逐步移植

核心原則

每一步都要有可驗證的基準點

不是「改到能編譯為止」,而是「從能運作的狀態開始,逐步添加功能」。

策略比較

項目直接升級從乾淨框架移植
起點現有專案(可能有歷史包袱)Spring Initializr 產生的乾淨專案
編譯錯誤一次全部出現逐步出現,易定位
配置正確性可能沿用舊配置使用官方建議配置
問題定位困難容易(每步可驗證)
工作量表面上較少表面上較多,但總體更可控

標準升級流程

Phase 0: 準備工作

目的:充分了解升級範圍,避免「事後發現遺漏」。

- [ ] 回退到穩定版本(確保有乾淨的起點)
- [ ] 建立升級紀錄文檔
- [ ] 盤點現有專案
- [ ] Java 模組清單(所有 package 目錄)
- [ ] resources 目錄內容(含 META-INF)
- [ ] 依賴清單(區分 api/implementation/compileOnly)
- [ ] 研究官方 Migration Guide
- [ ] 閱讀主框架 Migration Guide
- [ ] 閱讀相關框架 Migration Guide(Security, Hibernate 等)
- [ ] 建立「預期 Breaking Changes 清單」
- [ ] 標註影響的模組
- [ ] 區分「必須修復」vs「可延後處理」
經驗教訓

遺漏 META-INF/spring.factories 會導致啟動失敗但難以定位。事前完整盤點可避免此類問題。

Phase 1: 產生乾淨的消費者專案

目的:取得新版本的正確依賴配置,作為參考範本。

- [ ] 使用 Spring Initializr 產生專案
- 選擇目標 Spring Boot 版本
- 選擇所有需要的依賴
- [ ] 驗證可正常編譯和啟動
- [ ] 記錄 starter 名稱差異(與舊版對比)
- [ ] 記錄新增/移除的依賴

Phase 2: 建立 Library 專案骨架

目的:建立可編譯的 library 專案結構。

- [ ] 建立 java-library 專案結構
- [ ] 配置 build.gradle.kts
- plugins(java-library, maven-publish)
- repositories
- dependencies(參考 Phase 1 的配置)
- publishing 配置
- [ ] 配置 Composite Build(消費者引用 library)
- [ ] 驗證空專案可編譯

Phase 3: 逐模組移植 Library

目的:分批移植,每批都驗證,及早發現問題。

重要原則

不要一次複製所有模組。分批移植,每批:複製 → 編譯 → 修復 → 測試 → 驗證。

建議的移植順序(依賴關係由低到高):

批次 1: 基礎工具(無外部依賴)
- [ ] env/ - 環境配置
- [ ] bean/ - Bean 工具
- [ ] converter/ - 類型轉換器
→ 編譯驗證

批次 2: 序列化模組
- [ ] json/ - JSON 處理(可能有 Jackson Breaking Changes)
- [ ] xml/ - XML 處理
- [ ] csv/ - CSV 處理
→ 編譯驗證 + 執行單元測試

批次 3: 安全模組
- [ ] auth/ - 認證
- [ ] oauth2/ - OAuth2
- [ ] security/ - 安全工具
→ 編譯驗證 + 執行單元測試

批次 4: 資料模組
- [ ] entity/ - Entity 工具
- [ ] jpa/ - JPA 工具
- [ ] search/ - 搜尋
- [ ] cache/ - 快取
→ 編譯驗證 + 執行單元測試

批次 5: 其他模組
- [ ] file/, mail/, web/, http/, image/ 等
→ 編譯驗證 + 執行單元測試

批次 6: resources
- [ ] META-INF/(spring.factories 等)
- [ ] 其他 resources
→ 消費者啟動驗證

Phase 4: 整合驗證

目的:確保 library 可被消費者正常使用。

- [ ] 消費者專案引用 library
- [ ] 移除消費者的重複依賴
- [ ] 驗證消費者可啟動
- [ ] 驗證基本功能(Health check 等)

Phase 5: 移植 Application

目的:移植應用程式程式碼。

- [ ] 複製 Java 原始碼
- Entity → Repository → Service → Controller
- 每層複製後編譯驗證
- [ ] 複製 resources
- application.yml
- 其他配置檔
- [ ] 修復 Breaking Changes
- [ ] 執行單元測試
- [ ] 驗證啟動成功

Phase 6: 驗收

目的:確保升級後系統正常運作。

- [ ] 單元測試全部通過
- [ ] 功能驗收
- API 端點可存取
- 認證授權正常
- 資料庫操作正常
- [ ] 效能檢查(如適用)

Phase 7: Deprecation 處理(可選)

目的:處理編譯警告,為未來版本做準備。

- [ ] 處理 deprecation warnings
- [ ] 評估可選遷移項目
- [ ] 記錄決策和理由

區分 Breaking Change vs Deprecation

類型定義處理時機範例
Breaking Change不修就無法編譯/運作Phase 3-5 立即修復Jackson 套件名稱變更
Deprecation Warning可編譯但有警告Phase 7 處理EnvironmentPostProcessor 標註 deprecated
評估項目需研究是否遷移Phase 7 評估JJWT vs Nimbus
判斷原則

如果不處理會導致編譯失敗運作異常,就是 Breaking Change,必須立即處理。


Library vs Application 的差異

項目LibraryApplication
Gradle Pluginjava-libraryorg.springframework.boot
Main Class
可獨立啟動
驗證方式透過消費者驗證直接啟動驗證
依賴 Scopeapi(傳遞給消費者)implementation
發布方式Maven publishWAR/JAR 部署
bootJar Task停用啟用

Library 專案的特殊考量

Spring Initializr 不產生 library 專案,它只產生可啟動的應用程式。

因此,升級 library 時:

  1. 先用 Spring Initializr 產生消費者應用程式
  2. 參考消費者的依賴配置
  3. 手動建立 library 專案結構

升級紀錄的重要性

每次主版本升級都應該建立升級紀錄文檔,包含:

1. 升級計劃(事前)

  • 升級目標和範圍
  • 分階段的步驟清單
  • 預期的 breaking changes

2. 進度追蹤(過程中)

  • 用 checkbox 標記完成狀態
  • 記錄實際操作和發現

3. Breaking Changes 紀錄(過程中)

  • API 變更對照表
  • 解決方案和程式碼範例

4. 執行紀錄(過程中)

  • 按時間順序記錄操作
  • 遇到的問題和解決方式

升級紀錄範本

# [框架名稱] [版本] 升級紀錄

## 升級概覽
| 項目 | 升級前 | 升級後 |
|------|--------|--------|
| ... | ... | ... |

## 升級策略
(說明採用的策略和原因)

## 事前準備
### 模組清單
(盤點結果)

### 預期 Breaking Changes
| 項目 | 影響模組 | 處理方式 |
|------|---------|---------|
| ... | ... | ... |

## 升級進度
### Phase 0: 準備工作
- [ ] ...

### Phase 1: ...
- [ ] ...

## Breaking Changes 紀錄
| 變更類型 | 舊版 | 新版 | 說明 |
|---------|------|------|------|
| ... | ... | ... | ... |

## 執行紀錄
### YYYY-MM-DD
(實際操作紀錄)

相關文檔