從 Spring Boot 2.x 到 3.5.x + JDK21:一次完整的生產環境遷移實戰
@
- 從 Spring Boot 2.x 到 3.5.x + JDK21:一次完整的生產環境遷移實戰
- 升級背景
- 升級目標與核心變化
- 完整升級步驟
- 第一階段:準備工作(JDK 8 環境)
- 代碼分支管理
- 引入 OpenRewrite Maven 插件
- 執行自動化遷移
- OpenRewrite 自動完成的變更
- 增量合併場景處理
- 第二階段:環境切換(JDK 21 環境)
- 6. 修改 IDEA 項目配置
- 第一階段:準備工作(JDK 8 環境)
- 核心問題與解決方案
- 問題一:Hibernate DDL Auto 的陷阱
- 解決方案
- 問題二:Spring Security 配置遷移
- 問題三:SpringDoc OpenAPI 配置
- 問題四:依賴衝突與安全漏洞修復
- 問題五:URL 尾斜槓匹配策略變更
- 問題六:Apache POI / EasyExcel 升級
- 問題七:JDK 模塊化限制(--add-opens)
- 問題八:過期配置屬性警告
- 問題一:Hibernate DDL Auto 的陷阱
- 完整測試清單
- 升級感悟
- 最後:
升級背景
在私有化部署過程中,客户使用安全掃描工具檢測到大量安全漏洞,主要集中在:
- 框架版本過低:Spring Boot 2.1.6.RELEASE(發佈於 2019 年)
- JDK 版本過舊:JDK 8(缺乏最新安全補丁)
- 第三方依賴:多個依賴存在已知 CVE 漏洞
基於安全合規和長期維護的考慮,決定進行大版本升級。
- 當前版本:Spring Boot 2.1.6.RELEASE + JDK 8
- 目標版本:Spring Boot 3.5.4 + JDK 21 LTS
升級目標與核心變化
主要變化
| 類別 | 變化內容 | 遷移方式 |
|---|---|---|
| 命名空間 | javax.* → jakarta.* | 自動化遷移 |
| JDK 版本 | Java 8 → Java 21 LTS | 自動化遷移 + 手動調整 |
| 第三方依賴 | 大量依賴需要升級 | 手動處理 |
| API 文檔 | Swagger 2.x → SpringDoc OpenAPI 3.x | 配置調整 |
| 安全配置 | WebSecurityConfigurerAdapter 廢棄 | 重寫配置類 |
為什麼選擇自動化遷移?
前兩項(命名空間和 JDK 版本)涉及的代碼改動量極大,手動修改容易出錯且效率低下。
OpenRewrite 作為業界成熟的自動化重構工具,可以完成大部分繁瑣工作。
完整升級步驟
第一階段:準備工作(JDK 8 環境)
代碼分支管理
# 確保主分支代碼為最新
git checkout dev
git pull origin dev
# 創建升級專用分支
git checkout -b upgrade/springboot3-jdk21
引入 OpenRewrite Maven 插件
什麼是 OpenRewrite?
OpenRewrite 是一個自動化代碼重構和遷移工具,專為 Java 生態系統設計。
核心優勢:
- 精確安全:在 AST(抽象語法樹)層面操作,不會破壞代碼結構
- 批量處理:一次性處理整個代碼庫
- 可預覽:使用
rewrite:dryRun查看變更預覽 - 可定製:支持聲明式(YAML)或編程式自定義規則
工作原理:
OpenRewrite 通過解析源代碼生成無損語法樹(LST),在 AST 層面進行精確轉換,完整保留:
- 原始格式和縮進
- 所有註釋
- 代碼風格
配置方式:
在 pom.xml 的 <plugins> 節點下添加:
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>6.15.0</version>
<configuration>
<exportDatatables>true</exportDatatables>
<activeRecipes>
<!-- 升級到 Java 21 -->
<recipe>org.openrewrite.java.migrate.UpgradeToJava21</recipe>
<!-- JUnit 4 to 5 -->
<recipe>org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration</recipe>
<!-- Spring Boot 3.4(插件暫不支持 3.5,升級後手動改) -->
<recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_4</recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-migrate-java</artifactId>
<version>3.14.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-spring</artifactId>
<version>6.11.1</version>
</dependency>
</dependencies>
</plugin>
配方(Recipe)説明:
UpgradeSpringBoot_3_4 :升級至 Spring Boot 3.4.x(插件暫不支持 3.5,升級後手動修改版本號即可)UpgradeToJava21 :升級至 JDK 21(Spring Boot 配方僅升級到 JDK 17,需額外添加此配方)SpringBoot2JUnit4to5Migration :升級測試框架,避免自動化測試報錯
提示:你也可以編寫自定義配方來處理項目特定的遷移需求。
執行自動化遷移
mvn rewrite:run
或者在 IDEA 中通過 Maven 面板執行:

執行時間: 幾分鐘到幾十分鐘不等,取決於項目規模。
可能遇到的問題:
- 如果某些類包含特殊代碼導致報錯,可以先註釋掉,待升級完成後再處理
- 執行完成後可以刪除該插件(也可以保留,以便後續增量升級)
OpenRewrite 自動完成的變更
執行完成後,主要變化包括:
依賴升級:
pom.xml中的依賴版本自動升級- Spring Boot 版本升級到 3.4.x(手動改為 3.5.4)
包名變更:
javax.servlet.* → jakarta.servlet.*javax.persistence.* → jakarta.persistence.*javax.validation.* → jakarta.validation.*
API 文檔遷移:
- Swagger 2.x → SpringDoc OpenAPI 3.x
JDK 新特性應用:
- Text Blocks:多行字符串的優雅處理
// 自動轉換為
String json = """
{
"name": "user",
"age": 18
}
""";
- instanceof 模式匹配:簡化類型判斷和轉換
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}
- String.formatted(): 替代
String.format()
"Hello, %s!".formatted(name);
- 集合增強: getFirst() 替代 get(0)
- @Serial 註解:標記序列化相關字段
第三方庫升級:
- Apache HttpClient
- Apache Commons 系列
- 其他常用工具庫
增量合併場景處理
場景: 執行 Rewrite 後,舊分支又有代碼提交,合併時出現大量 javax 包名和 Swagger 註解衝突。
解決方案:使用 IntelliJ IDEA 自帶的 Refactor 功能(本質也是基於 OpenRewrite)
操作步驟:
- 打開 IDEA,選擇
Refactor → Migrate Packages and Classes - 選擇遷移規則(javax → jakarta)
- 預覽變更並執行


第二階段:環境切換(JDK 21 環境)
重要分界線:以下操作需在 JDK 21 環境下進行。
6. 修改 IDEA 項目配置
修改 SDK 和 Language Level(快捷鍵:Ctrl + Alt + Shift + S):

修改 Modules 的 Language Level:

修改 Java Compiler(快捷鍵:Ctrl + Alt + S):

核心問題與解決方案
問題一:Hibernate DDL Auto 的陷阱
嚴重警告:在完成以下配置前,切勿啓動項目!否則可能導致數據庫結構被錯誤修改。
問題背景
新舊版本 Hibernate 的行為差異:

為什麼要禁用?
在生產環境中使用 spring.jpa.hibernate.ddl-auto=update 存在嚴重風險:
- 數據安全風險:自動更新可能導致意外的數據丟失或結構變更
- 性能問題:啓動時全表檢查會顯著增加應用啓動時間
- 版本控制缺失:無法追蹤數據庫變更歷史,不利於團隊協作和回滾
- 升級後風險更高:Hibernate 6.x 的校驗更嚴格,誤操作概率增加
解決方案
方案一:配置優先級控制(推薦)
在 CI/CD 啓動腳本中設置 VM 參數:
java -jar app.jar -Dspring.jpa.hibernate.ddl-auto=none
優先級:VM 參數 > 配置中心(Apollo/Nacos) > application.properties
方案二:使用專業的數據庫版本管理工具
推薦使用 Flyway 或 Liquibase 管理數據庫腳本:
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
方案三:結構對比工具
- Navicat:提供結構同步功能
- DataGrip:IntelliJ 系產品,支持數據庫結構對比
問題二:Spring Security 配置遷移
核心變化
WebSecurityConfigurerAdapter已廢棄- 推薦使用 Lambda DSL 配置方式
- 配置方式從繼承改為 Bean 註冊
遷移示例
舊版配置(Spring Security 5.x):
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.anyRequest().authenticated();
}
}
新版配置(Spring Security 6.x):
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final TokenProvider tokenProvider;
public SecurityConfig(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(sessionManagement -> sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorizeRequests -> authorizeRequests
// 允許所有 OPTIONS 請求
.requestMatchers(OPTIONS, "**").permitAll()
.requestMatchers(
"/swagger-ui/**",
"/v3/api-docs/**",
"/swagger-resources/**",
"/images/**",
"/webjars/**").permitAll()
.anyRequest().authenticated())
.addFilterBefore(new JWTFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
RequestMatcher 調整注意事項
- 新增 SpringDoc 路徑(必須)
/swagger-ui/**
/v3/api-docs/**
修正通配符寫法:
❌ 錯誤: //**/*.js
✅ 正確: /**/*.js
否則會拋出 PatternParseException
問題三:SpringDoc OpenAPI 配置
Swagger → SpringDoc 遷移
<!-- 移除舊的 Swagger 依賴 -->
<!--
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
-->
<!-- 添加新的 SpringDoc 依賴 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
配置示例
@Configuration
@OpenAPIDefinition
public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
OpenAPI openAPI = new OpenAPI();
openAPI.info(new Info().title("API 文檔").version("1.0"));
// 配置 Authorization 登錄鑑權
Map<String, SecurityScheme> map = Map.of("Authorization",
new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.in(SecurityScheme.In.HEADER)
.name("Authorization"));
openAPI.components(new Components().securitySchemes(map));
map.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key)));
return openAPI;
}
}
註解對應關係
| Swagger 2.x | SpringDoc OpenAPI 3.x |
|---|---|
| @Api | @Tag |
| @ApiOperation | @Operation |
| @ApiParam | @Parameter |
| @ApiModel | @Schema |
| @ApiModelProperty | @Schema |
訪問地址變更
原 Swagger UI 地址: http://localhost:8080/swagger-ui.html
新 SpringDoc 地址: http://localhost:8080/swagger-ui/index.html
問題四:依賴衝突與安全漏洞修復
檢測工具
使用 IDEA 自帶的依賴分析工具:

必須升級的依賴(存在高危漏洞)
推薦使用 OWASP Dependency-Check 或 Snyk 掃描:
mvn dependency-check:check
解決依賴衝突的技巧
問題:Maven 依賴解析採用"最短路徑優先"和"第一聲明優先"原則,可能導致舊版本覆蓋新版本。
解決方案:顯式聲明期望的版本
<dependencies>
<!-- 顯式聲明 Spring Framework 版本,避免被傳遞依賴覆蓋 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.1.3</version>
</dependency>
</dependencies>
快速檢測技巧:
在 IDEA 的 Maven 依賴樹中搜索 RELEASE ,Spring 新版本已不使用 RELEASE 後綴,搜索到的基本都是舊版本。

問題五:URL 尾斜槓匹配策略變更
行為變化
| 版本 | 行為 |
|---|---|
| Spring Boot 2.x | /api/user/get 和 /api/user/get/ 視為同一接口 |
| Spring Boot 3.x | /api/user/get 和 /api/user/get/ 視為不同接口 |
常見導致尾斜槓的情況
- Case 1:類註解帶尾斜槓
@RequestMapping("/api/user/")
public class UserController {
@PostMapping("login") // 實際路徑:/api/user/login
}
Case 2:空字符串映射
@RequestMapping("/api/user")
public class UserController {
@PostMapping("") // 實際路徑:/api/user/(帶尾斜槓!)
}
Case 3:根路徑映射
@PostMapping("/") // 實際路徑:/(帶尾斜槓)
**** 檢查方式
- IDEA Endpoints 工具窗口:查看所有端點
- SpringDoc UI:訪問 Swagger 頁面檢查

臨時解決方案(不推薦長期使用)
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// 設 置 為 true 以 忽 略 尾 斜 槓 , 恢 復 舊 版 本 行 為
configurer.setUseTrailingSlashMatch(true);
}
}
注意__: ·
setUseTrailingSlashMatch在 Spring 6.x 後已標記為廢棄,後續版本將刪除。建議逐步修正所有端點,去除尾斜槓。
根本解決方案
- 修正所有 Controller 的路徑映射
- 通知前端團隊同步修改調用路徑
- 如果有硬編碼的 URL,全局搜索並修正
- 使用測試確保前後端調用正常
問題六:Apache POI / EasyExcel 升級
背景
Apache POI 舊版本(< 5.0)存在多個 CVE 安全漏洞,必須升級。
推薦方案
對於新項目:直接使用 FastExcel
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>1.0.0</version>
</dependency>
對於使用 EasyExcel 的舊項目:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.3</version>
</dependency>
説明:EasyExcel 已不再維護,FastExcel 是社區維護的替代方案,API 基本兼容。
遷移注意事項
EasyExcel 跨大版本升級(2.x → 4.x)API 變化較大,主要改動:
1. 監聽器接口方法簽名調整
2. 部分工具類包路徑變更
3. 自定義轉換器需要適配新接口
建議參考官方遷移文檔:EasyExcel官方文檔 - 基於Java的Excel處理工具 | Easy Excel 官網
問題七:JDK 模塊化限制(--add-opens)
問題現象
某些依賴庫使用反射訪問 JDK 內部 API,在 JDK 9+ 模塊化系統下會報錯:
InaccessibleObjectException: Unable to make field accessible:
module java.base does not "opens java.net" to unnamed module
解決方案
在 IDEA 運行配置中添加 VM 參數:
開啓 VM 參數配置(默認隱藏):

解決方案
在 IDEA 運行配置中添加 VM 參數:
開啓 VM 參數配置(默認隱藏):

常見需要開放的模塊
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
問題八:過期配置屬性警告
問題現象
啓動時出現警告:
Property 'spring.xxx.yyy' is deprecated

解決方案:
-
查看 Spring Boot 官方遷移文檔
-
使用 IDEA 的智能提示查看替代屬性
-
修改配置文件( 常見過期屬性:
application.yml或配置中心)
常見過期屬性:
| 過期屬性 | 替代屬性 |
|---|---|
| spring.datasource.type | 自動推斷,無需配置 |
| spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults | 已移除 |
| management.metrics.export.prometheus.enabled | management.prometheus.metrics.export.enabled |
完整測試清單
升級完成後,務必進行全面的迴歸測試:
- Spring Security:認證、授權是否正常
- SpringDoc:API 文檔是否可訪問( /swagger-ui/index.html )
- 數據庫操作:JPA/MyBatis 是否正常工作
- 緩存:Redis/Caffeine 等緩存是否生效
- 消息隊列:RabbitMQ/Kafka 等是否正常
- 定時任務:Scheduled/Quartz 是否按預期執行
- 文件上傳/下載:文件 IO 操作是否正常
- 業務功能:核心業務流程是否正常(重點關注有代碼改動的地方)
- 性能測試:對比升級前後的性能指標
升級感悟
框架層面的變化趨勢
通過這次升級,我觀察到現代框架的一些發展趨勢:
- 校驗更嚴格:
- Spring 不再容忍 URL 尾斜槓的模糊匹配
- 循環依賴檢測更嚴格(默認禁止)
- Hibernate 對實體狀態的校驗更精確
- 安全性優先:
- 默認配置更保守
- 廢棄不安全的 API
- 強制升級修復已知漏洞
- 現代化 API:
- Lambda DSL 配置風格
- 函數式編程支持
- 更簡潔的 API 設計
依賴選擇建議
基於這次升級經驗,對於第三方庫的選擇建議:
優先選擇:
✅ 國際主流項目(Apache、Spring 生態等)
✅ 有完善文檔和測試的項目
✅ 活躍維護且社區規模大的項目
✅ 語義化版本管理清晰的項目
謹慎選擇:
⚠️ 缺乏自動化測試的項目
⚠️ 長期未更新的項目
⚠️ API 設計不穩定、頻繁 Breaking Change 的項目
⚠️ 文檔不全、維護團隊不穩定的項目
自動化遷移的價值
OpenRewrite 等自動化工具在大版本升級中的價值無可替代:
- 減少 90% 以上的機械性改動
- 避免手工替換導致的遺漏
- 保持代碼風格和註釋
- 降低升級風險
建議在日常開發中也關注此類工具,提升團隊整體效率。
本文首發於 [尚硅谷編程聯盟],轉載請註明出處。
最後:
“在這個最後的篇章中,我要表達我對每一位讀者的感激之情。你們的關注和回覆是我創作的動力源泉,我從你們身上吸取了無盡的靈感與勇氣。我會將你們的鼓勵留在心底,繼續在其他的領域奮鬥。感謝你們,我們總會在某個時刻再次相遇。”
