Spring Boot 2 到 3 升级详细步骤
项目升级概览
基于 公司科创项目
的实际升级经验,本文档详细记录了从 Spring Boot 2.7.0 升级到 Spring Boot 3.2.5 的完整过程。
升级时间线
- 升级前版本: Spring Boot 2.7.0
- 升级后版本: Spring Boot 3.2.5
- Java 版本: 升级到 Java 17
- 主要升级时间: 2025年6月
第一步:环境准备
1.1 Java 环境升级
检查当前 Java 版本:
java -version
安装 Java 17:
# macOS (使用 Homebrew)
brew install openjdk@17
# 或者下载官方 JDK 17
# https://www.oracle.com/java/technologies/downloads/#java17
设置 JAVA_HOME:
export JAVA_HOME=/Library/Java/JavaVirtualMachines/openjdk-17.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH
1.2 Maven 配置更新
更新 Maven 编译器插件:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
<release>17</release>
<parameters>true</parameters>
<compilerArgs>
<arg>--add-opens</arg>
<arg>jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
<arg>--add-opens</arg>
<arg>jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
</compilerArgs>
</configuration>
</plugin>
第二步:核心依赖升级
2.1 Spring Boot 版本升级
父 POM 更新:
<properties>
<spring-boot.version>3.2.5</spring-boot.version>
<java.version>17</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Spring Cloud 版本升级:
<properties>
<spring-cloud.version>2022.0.4</spring-cloud.version>
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
</properties>
2.2 数据库相关依赖升级
MyBatis Plus 升级:
<!-- 升级前 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- 升级后 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
重要变更:
- 主键生成策略
ID_WORKER_STR
被废弃,使用ASSIGN_ID
替代 selectCount
方法不再返回int
类型,现在返回long
类型
// 升级前
@TableId(type = IdType.ID_WORKER_STR)
private String id;
int count = userMapper.selectCount(wrapper);
// 升级后
@TableId(type = IdType.ASSIGN_ID)
private String id;
long count = userMapper.selectCount(wrapper);
动态数据源升级:
<!-- 升级前 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 升级后 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>4.3.1</version>
</dependency>
注意: 如果不升级到 4.3.1 版本,可能会出现数据源配置读取失败的错误:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Druid 连接池升级:
<!-- 升级前 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.9</version>
</dependency>
<!-- 升级后 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.25</version>
</dependency>
分页插件升级:
<!-- 升级前 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!-- 升级后 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
ShardingSphere 升级:
<!-- 升级前 -->
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
<!-- 升级后 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.0</version>
</dependency>
注意: 如果不升级到 5.1.0 版本,可能读取不到数据库配置。
第三步:包名迁移 (javax → jakarta)
3.1 核心包名变更
这是升级过程中最重要的变更之一。Spring Boot 3 将所有的 javax.*
包迁移到 jakarta.*
。
主要变更列表:
升级前 | 升级后 |
---|---|
javax.annotation.* | jakarta.annotation.* |
javax.servlet.* | jakarta.servlet.* |
javax.persistence.* | jakarta.persistence.* |
javax.validation.* | jakarta.validation.* |
javax.transaction.* | jakarta.transaction.* |
javax.xml.* | jakarta.xml.* |
3.2 具体代码变更示例
注解变更:
// 升级前
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
// 升级后
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
Servlet 相关变更:
// 升级前
import javax.servlet.Servlet;
import javax.servlet.Filter;
import javax.servlet.ServletRegistrationBean;
// 升级后
import jakarta.servlet.Servlet;
import jakarta.servlet.Filter;
import jakarta.servlet.ServletRegistrationBean;
Druid 配置变更:
// 升级前
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
// 升级后
import com.alibaba.druid.support.jakarta.StatViewServlet;
import com.alibaba.druid.support.jakarta.WebStatFilter;
3.3 批量替换脚本
可以使用以下命令批量替换包名:
# 替换 javax.annotation 为 jakarta.annotation
find . -name "*.java" -exec sed -i '' 's/javax\.annotation/jakarta.annotation/g' {} \;
# 替换 javax.servlet 为 jakarta.servlet
find . -name "*.java" -exec sed -i '' 's/javax\.servlet/jakarta.servlet/g' {} \;
# 替换 javax.persistence 为 jakarta.persistence
find . -name "*.java" -exec sed -i '' 's/javax\.persistence/jakarta.persistence/g' {} \;
第四步:API 文档框架迁移
4.1 Swagger 到 SpringDoc OpenAPI
依赖变更:
<!-- 移除旧的 Swagger 依赖 -->
<!--
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
-->
<!-- 添加新的 SpringDoc OpenAPI 依赖 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.8</version>
</dependency>
注解映射表:
Swagger 注解 | SpringDoc OpenAPI 注解 |
---|---|
@Api | @Tag |
@ApiOperation | @Operation |
@ApiParam | @Parameter |
@ApiModel | @Schema |
@ApiModelProperty | @Schema |
@ApiResponse | @ApiResponse |
@ApiResponses | @ApiResponses |
具体代码变更:
// 升级前
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@Api(tags = "用户管理")
@RestController
public class UserController {
@ApiOperation("获取用户信息")
@GetMapping("/user/{id}")
public User getUser(@ApiParam("用户ID") @PathVariable String id) {
return userService.getUser(id);
}
}
// 升级后
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@Tag(name = "用户管理")
@RestController
public class UserController {
@Operation(summary = "获取用户信息")
@GetMapping("/user/{id}")
public User getUser(@Parameter(description = "用户ID") @PathVariable String id) {
return userService.getUser(id);
}
}
4.2 Knife4j 升级
依赖变更:
<!-- 升级前 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.8</version>
</dependency>
<!-- 升级后 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
注意: Knife4j 4.4.0 版本较高,公司私服和阿里云镜像中可能不存在,需要添加镜像源:
<repository>
<id>central</id>
<name>Maven Central</name>
<url>https://repo1.maven.org/maven2</url>
</repository>
第五步:安全认证框架升级
5.1 Sa-Token 升级
依赖变更:
<!-- 升级前 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.31.0</version>
</dependency>
<!-- 升级后 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>1.34.0</version>
</dependency>
代码变更:
// 升级前
import cn.dev33.satoken.id.SaIdUtil;
public class TokenUtil {
public static String getInnerAuthToken() {
return SaIdUtil.getToken();
}
public static void checkInnerToken(String token) {
SaIdUtil.checkToken(token);
}
}
// 升级后
import cn.dev33.satoken.stp.StpUtil;
public class TokenUtil {
public static String getInnerAuthToken() {
return StpUtil.getTokenValue();
}
public static void checkInnerToken(String token) {
// 根据新版本API调整
// SaIdUtil.checkToken(token);
}
}
5.2 Shiro 升级
依赖变更:
<!-- 升级前 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
<!-- 升级后 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-jakarta-ee</artifactId>
<version>2.0.4</version>
</dependency>
注意: shiro-spring-boot-starter
2.0.4 不再支持 jakarta,需要使用 shiro-jakarta-ee
。
5.3 OAuth2 升级
升级到 Spring Authorization Server:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
</dependency>
注意: 授权中心需要重构,使用 Spring Authorization Server 替换原有的 OAuth2 实现。
5.4 JWT 升级
依赖变更:
<!-- 升级前 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- 升级后 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
代码变更:
// 升级前
Claims claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
// 升级后
SecretKey key = Keys.hmacShaKeyFor(TokenConstants.SECRET.getBytes());
JwtParser parser = Jwts.parser().verifyWith(key).build();
Claims claims = parser.parseSignedClaims(token).getPayload();
第六步:工具库升级
6.1 常用工具库版本升级
<properties>
<!-- Apache Commons -->
<common-lang3.version>3.13.0</common-lang3.version>
<commons-io.version>2.15.1</commons-io.version>
<commons-text.version>1.11.0</commons-text.version>
<!-- JSON 处理 -->
<fastjson.version>2.0.43</fastjson.version>
<jackson.version>2.15.3</jackson.version>
<!-- 其他工具库 -->
<hutool-all.version>5.8.24</hutool-all.version>
<guava.version>33.0.0-jre</guava.version>
<okhttp3.version>4.12.0</okhttp3.version>
<lombok.version>1.18.30</lombok.version>
</properties>
6.2 Apache POI 升级
依赖变更:
<!-- 升级前 -->
<poi.version>4.1.2</poi.version>
<!-- 升级后 -->
<poi.version>5.2.3</poi.version>
6.3 包名变更处理
Apache Commons Lang:
// 升级前
import org.apache.commons.lang.StringUtils;
// 升级后
import org.apache.commons.lang3.StringUtils;
6.4 Base64 编码解码变更
问题: Cannot resolve symbol 'BASE64Decoder'
解决方案: 使用标准的 java.util.Base64
替代
// 升级前(解码)
static BASE64Decoder decoder = new BASE64Decoder();
byte[] b = decoder.decodeBuffer(regReplaceBase64(imgStr));
// 升级后(解码)
byte[] b = Base64.getDecoder().decode(regReplaceBase64(imgStr));
// 升级前(编码)
BASE64Encoder encode = new BASE64Encoder();
String s = encode.encode(data);
// 升级后(编码)
String s = Base64.getEncoder().encodeToString(data);
6.5 XPath 包名变更
问题: Package 'com.sun.org.apache.xpath.internal.operations' is declared in module 'java.xml', which does not export it to the unnamed module
解决方案: 使用 javax.xml.xpath
替代
// 升级前
import com.sun.org.apache.xpath.internal.operations.String;
// 升级后
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
第七步:配置类调整
7.1 动态数据源配置调整
配置类变更:
// 升级前
@Configuration
public class MybatisPlusConfig {
@Primary
@Bean(name = "dataSourceSystem")
public DataSource dataSourceOne(DynamicDataSourceProperties properties) throws SQLException, IOException, URISyntaxException {
DataSource dataSource = dynamicDataSource(properties);
initDynamicDataSource(dataSource, properties);
return dataSource;
}
}
// 升级后
@Configuration
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
public class MybatisPlusConfig {
private final DynamicDataSourceProperties dynamicDataSourceProperties;
public MybatisPlusConfig(DynamicDataSourceProperties dynamicDataSourceProperties) {
this.dynamicDataSourceProperties = dynamicDataSourceProperties;
}
@Primary
@Bean
public DynamicDataSourceProperties primaryDynamicDataSourceProperties() {
return dynamicDataSourceProperties;
}
@Primary
@Bean(name = "dataSourceSystem")
public DataSource dataSourceOne() throws SQLException, IOException, URISyntaxException {
DataSource dataSource = dynamicDataSource(dynamicDataSourceProperties);
initDynamicDataSource(dataSource, dynamicDataSourceProperties);
return dataSource;
}
}
7.2 Druid 配置调整
Druid 配置类变更:
// 升级前
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// ...
}
// 升级后
import com.alibaba.druid.support.jakarta.StatViewServlet;
import com.alibaba.druid.support.jakarta.WebStatFilter;
import jakarta.servlet.Filter;
import jakarta.servlet.Servlet;
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// ...
}
7.3 循环依赖配置
应用配置调整:
# 解决循环依赖问题
spring:
main:
allow-circular-references: true
7.4 参数名发现器变更
LocalVariableTableParameterNameDiscoverer 被废弃:
// 升级前
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
// 升级后
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
第八步:异常处理调整
8.1 移除不必要的异常声明
// 升级前
public static Connection getConn(DbLinkEntity dbLinkEntity) throws DataException {
try{
return new PrepSqlDTO().withConn(dbLinkEntity).switchConn().getConnection();
}catch (SQLException d){
throw new DataException("数据库连接失败");
}
}
// 升级后
public static Connection getConn(DbLinkEntity dbLinkEntity) {
try{
return new PrepSqlDTO().withConn(dbLinkEntity).switchConn().getConnection();
}catch (SQLException d){
throw new RuntimeException("数据库连接失败", d);
}
}
第九步:测试和验证
9.1 编译测试
# 清理并编译
mvn clean compile
# 运行测试
mvn test
# 打包
mvn package -DskipTests
9.2 功能测试
- 数据库连接测试
- API 接口测试
- 认证授权测试
- 文件上传下载测试
9.3 性能测试
- 启动时间对比
- 内存使用情况
- 响应时间对比
第十步:常见问题解决
10.1 Lombok 编译问题
问题: Unable to make field private com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors com.sun.tools.javac.processing.JavacProcessingEnvironment.discoveredProcs accessible: module jdk.compiler does not "opens com.sun.tools.javac.processing" to unnamed module
解决方案:
- 升级 Lombok 到 1.18.30
- 升级 maven-compiler-plugin 到 3.11.0
- 添加编译器参数
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<release>17</release>
<compilerArgs>
<arg>--add-opens</arg>
<arg>jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
<arg>--add-opens</arg>
<arg>jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
</compilerArgs>
</configuration>
</plugin>
10.2 包名冲突问题
问题: 编译时出现 javax.*
和 jakarta.*
包冲突
解决方案:
<dependency>
<groupId>conflicting-library</groupId>
<artifactId>conflicting-artifact</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
10.3 配置类加载失败
问题: 配置类无法正确加载
解决方案:
- 检查包名是否正确
- 确保使用
jakarta.*
包 - 检查依赖版本兼容性
10.4 数据库连接问题
问题: 数据库连接失败
解决方案:
- 检查驱动版本兼容性
- 更新连接配置
- 验证数据库服务状态
10.5 Maven 打包问题
问题: Maven 打包时出现模块访问权限错误
解决方案: 添加 VM 参数
mvn clean package -Dmaven.compiler.fork=true -Dmaven.compiler.compilerArgs="--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
升级检查清单
升级前检查
- 创建代码备份分支
- 记录当前系统状态
- 准备测试环境
- 确认 Java 17 环境
- 备份数据库配置
升级过程检查
- 更新 Spring Boot 版本
- 更新 Java 版本配置
- 迁移包名 (javax → jakarta)
- 更新数据库相关依赖
- 迁移 API 文档框架
- 更新安全认证框架
- 升级工具库版本
- 调整配置文件
- 修复编译错误
- 运行单元测试
- 升级 Lombok 和编译器插件
- 处理 Base64 编码解码变更
- 更新 XPath 包名
- 替换废弃的类和方法
- 配置循环依赖允许
- 更新 JWT 解析方式
升级后验证
- 编译通过
- 单元测试通过
- 集成测试通过
- 功能测试通过
- 性能测试通过
- 文档更新
- 部署验证
总结
通过本项目的实际升级经验,Spring Boot 2 到 3 的升级主要涉及以下几个方面:
- 环境升级: Java 17 + Maven 3.11.0
- 包名迁移: javax → jakarta
- 依赖升级: 多个核心依赖需要升级到兼容版本
- API 变更: 部分 API 接口发生变化
- 配置调整: 配置文件需要相应调整
- 工具库变更: Base64、XPath 等工具类的使用方式发生变化
- 框架重构: 授权中心、JWT 解析等需要重构
升级过程中需要特别注意:
- 逐步进行,避免一次性修改过多内容
- 充分测试每个模块的变更
- 保持良好的版本控制和回滚机制
- 及时更新相关文档
- 注意依赖版本查询:https://mvnrepository.com/