@
在私有化部署过程中,客户使用安全扫描工具检测到大量安全漏洞,主要集中在:
基于安全合规和长期维护的考虑,决定进行大版本升级。
主要变化
| 类别 | 变化内容 | 迁移方式 |
|---|---|---|
| 命名空间 | javax.* → jakarta.* | 自动化迁移 |
| JDK 版本 | Java 8 → Java 21 LTS | 自动化迁移 + 手动调整 |
| 第三方依赖 | 大量依赖需要升级 | 手动处理 |
| API 文档 | Swagger 2.x → SpringDoc OpenAPI 3.x | 配置调整 |
| 安全配置 | WebSecurityConfigurerAdapter 废弃 | 重写配置类 |
为什么选择自动化迁移?
前两项(命名空间和 JDK 版本)涉及的代码改动量极大,手动修改容易出错且效率低下。
OpenRewrite 作为业界成熟的自动化重构工具,可以完成大部分繁琐工作。
# 确保主分支代码为最新
git checkout dev
git pull origin dev
# 创建升级专用分支
git checkout -b upgrade/springboot3-jdk21
什么是 OpenRewrite?
OpenRewrite 是一个自动化代码重构和迁移工具,专为 Java 生态系统设计。
核心优势:
rewrite:dryRun 查看变更预览 工作原理:
OpenRewrite 通过解析源代码生成无损语法树(LST),在 AST 层面进行精确转换,完整保留:
配置方式:
在 pom.xml 的 节点下添加:
org.openrewrite.maven
rewrite-maven-plugin
6.15.0
true
org.openrewrite.java.migrate.UpgradeToJava21
org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration
org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_4
org.openrewrite.recipe
rewrite-migrate-java
3.14.1
runtime
org.openrewrite.recipe
rewrite-spring
6.11.1
配方(Recipe)说明:
UpgradeSpringBoot_3_4 :升级至 Spring Boot 3.4.x(插件暂不支持 3.5,升级后手动修改版本号即可) UpgradeToJava21 : 升级至 JDK 21(Spring Boot 配方仅升级到 JDK 17,需额外添加此配方) SpringBoot2JUnit4to5Migration :升级测试框架,避免自动化测试报错提示:你也可以编写自定义配方来处理项目特定的迁移需求。
mvn rewrite:run
或者在 IDEA 中通过 Maven 面板执行:

执行时间: 几分钟到几十分钟不等,取决于项目规模。
可能遇到的问题:
执行完成后,主要变化包括:
依赖升级:
pom.xml 中的依赖版本自动升级 包名变更:
javax.servlet.* → jakarta.servlet.*javax.persistence.* → jakarta.persistence.* javax.validation.* → jakarta.validation.*API 文档迁移:
JDK 新特性应用:
// 自动转换为
String json = """
{
"name": "user",
"age": 18
}
""";
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}
String.format()"Hello, %s!".formatted(name);
第三方库升级:
场景: 执行 Rewrite 后,旧分支又有代码提交,合并时出现大量 javax 包名和 Swagger 注解冲突。
解决方案:使用 IntelliJ IDEA 自带的 Refactor 功能(本质也是基于 OpenRewrite)
操作步骤:
Refactor → Migrate Packages and Classes 

重要分界线:以下操作需在 JDK 21 环境下进行。
修改 SDK 和 Language Level(快捷键:Ctrl + Alt + Shift + S):

修改 Modules 的 Language Level:

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

严重警告:在完成以下配置前,切勿启动项目!否则可能导致数据库结构被错误修改。
问题背景
新旧版本 Hibernate 的行为差异:

为什么要禁用?
在生产环境中使用 spring.jpa.hibernate.ddl-auto=update 存在严重风险:
方案一:配置优先级控制(推荐)
在 CI/CD 启动脚本中设置 VM 参数:
java -jar app.jar -Dspring.jpa.hibernate.ddl-auto=none
优先级:VM 参数 > 配置中心(Apollo/Nacos) > application.properties
方案二:使用专业的数据库版本管理工具
推荐使用 Flyway 或 Liquibase 管理数据库脚本:
org.flywaydb
flyway-core
方案三:结构对比工具
核心变化
WebSecurityConfigurerAdapter 已废弃迁移示例
旧版配置(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 调整注意事项
/swagger-ui/**
/v3/api-docs/**
修正通配符写法:
❌ 错误: //**/*.js
✅ 正确: /**/*.js
否则会抛出 PatternParseException
Swagger → SpringDoc 迁移
org.springdoc
springdoc-openapi-starter-webmvc-ui
2.3.0
配置示例
@Configuration
@OpenAPIDefinition
public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
OpenAPI openAPI = new OpenAPI();
openAPI.info(new Info().title("API 文档").version("1.0"));
// 配置 Authorization 登录鉴权
Map 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 依赖解析采用"最短路径优先"和"第一声明优先"原则,可能导致旧版本覆盖新版本。
解决方案:显式声明期望的版本
org.springframework
spring-core
6.1.3
快速检测技巧:
在 IDEA 的 Maven 依赖树中搜索 RELEASE ,Spring 新版本已不使用 RELEASE 后缀,搜索到的基本都是旧版本。

行为变化
| 版本 | 行为 |
|---|---|
| Spring Boot 2.x | /api/user/get 和 /api/user/get/ 视为同一接口 |
| Spring Boot 3.x | /api/user/get 和 /api/user/get/ 视为不同接口 |
常见导致尾斜杠的情况
@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("/") // 实际路径:/(带尾斜杠)
**** 检查方式

临时解决方案(不推荐长期使用)
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 后已标记为废弃,后续版本将删除。建议逐步修正所有端点,去除尾斜杠。
根本解决方案
背景
Apache POI 旧版本(
推荐方案
对于新项目:直接使用 FastExcel
cn.idev.excel
fastexcel
1.0.0
对于使用 EasyExcel 的旧项目:
com.alibaba
easyexcel
4.0.3
说明:EasyExcel 已不再维护,FastExcel 是社区维护的替代方案,API 基本兼容。
迁移注意事项
EasyExcel 跨大版本升级(2.x → 4.x)API 变化较大,主要改动:
1. 监听器接口方法签名调整
2. 部分工具类包路径变更
3. 自定义转换器需要适配新接口
建议参考官方迁移文档:EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网
问题现象
某些依赖库使用反射访问 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 |
升级完成后,务必进行全面的回归测试:
框架层面的变化趋势
通过这次升级,我观察到现代框架的一些发展趋势:
依赖选择建议
基于这次升级经验,对于第三方库的选择建议:
优先选择:
✅ 国际主流项目(Apache、Spring 生态等)
✅ 有完善文档和测试的项目
✅ 活跃维护且社区规模大的项目
✅ 语义化版本管理清晰的项目
谨慎选择:
⚠️ 缺乏自动化测试的项目
⚠️ 长期未更新的项目
⚠️ API 设计不稳定、频繁 Breaking Change 的项目
⚠️ 文档不全、维护团队不稳定的项目
自动化迁移的价值
OpenRewrite 等自动化工具在大版本升级中的价值无可替代:
建议在日常开发中也关注此类工具,提升团队整体效率。
本文首发于 [尚硅谷编程联盟],转载请注明出处。
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”
登录查看全部
参与评论
手机查看
返回顶部