欢迎光临
我们一直在努力

Spring Boot 3.2升级实战:从2.7平稳迁移的10个关键步骤

前言:为什么现在必须升级到Spring Boot 3.2?

如果你还在使用Spring Boot 2.7.x,你可能面临这些问题:

  • 🚫 2024年8月后Spring Boot 2.7将停止维护(包括安全更新)

  • 🚫 无法使用JDK 21的虚拟线程等新特性

  • 🚫 错过Spring Framework 6的性能提升(响应速度提升40%)

  • 🚫 无法集成最新AI、云原生技术栈

但升级过程确实充满挑战:包名从javax到jakarta、配置项大规模变更、第三方依赖兼容性问题…

本文将带你用最小的代价完成从2.7到3.2的平稳升级,每个步骤都包含可执行的代码和验证方法。

升级前准备:风险评估与检查清单

1.1 项目健康度检查

在开始升级前,先运行以下检查:

# 1. 检查当前依赖健康状况
mvn dependency:tree | grep -E "(javax|log4j|logback|spring-security)" > deps-audit.txt

# 2. 运行完整测试套件
mvn clean test -DskipTests=false

# 3. 生成API变更报告
mvn versions:display-plugin-updates
mvn versions:display-dependency-updates

1.2 必须完成的准备工作

✅ 备份完整代码:确保有可回退的版本

git checkout -b spring-boot-3-upgrade
git add .
git commit -m "Backup before Spring Boot 3.2 upgrade"

✅ 记录关键配置:特别关注以下文件:

  • application.properties / application.yml

  • pom.xml 中的依赖版本

  • 自定义的@Configuration类

  • 过滤器、拦截器配置

✅ 建立性能基线:

# 记录当前应用启动时间
time mvn spring-boot:run

# 记录关键API响应时间(使用JMeter或简单脚本)

第1步:JDK版本升级(从JDK 8/11到JDK 17+)

1.1 修改pom.xml中的JDK配置

<!– 在pom.xml的properties部分修改 –>
<properties>
<!– 从JDK 8/11升级到JDK 17 –>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>

<!– 可选:启用预览特性(如需使用虚拟线程) –>
<maven.compiler.compilerArgs>
<arg>–enable-preview</arg>
</maven.compiler.compilerArgs>
</properties>

1.2 处理JDK 17的模块系统问题

如果你的项目使用了反射或动态代理,需要在module-info.java中添加:

module your.application {
requires spring.core;
requires spring.context;
requires spring.web;

// 允许反射访问
opens com.yourpackage to spring.core, spring.beans, spring.context;

// 如果使用Jackson
opens com.yourpackage.model to com.fasterxml.jackson.databind;
}

或者,在启动参数中添加(临时方案):

-Dspring.native.remove-yaml-support=false \\
–add-opens java.base/java.lang=ALL-UNNAMED \\
–add-opens java.base/java.util=ALL-UNNAMED

第2步:Spring Boot父POM版本升级

2.1 修改父依赖版本

<!– 从2.7.x升级到3.2.4 –>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!– 从原来的2.7.x改为 –>
<version>3.2.4</version>
<relativePath/>
</parent>

<!– 或者使用dependencyManagement方式 –>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

2.2 处理废弃的依赖

Spring Boot 3.2移除了以下starter,需要手动添加:

<!– 如果使用了以下starter,需要单独引入 –>
<dependencies>
<!– 1. spring-boot-starter-log4j2 (如果使用Log4j2) –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

<!– 2. 如果使用了spring-boot-starter-tomcat(现在内嵌在web中) –>
<!– 无需单独声明 –>
</dependencies>

第3步:javax到jakarta的包名迁移

3.1 使用IDE的全局替换功能

在IntelliJ IDEA或Eclipse中执行全局替换:

  • 查找:javax.persistence → 替换为:jakarta.persistence

  • 查找:javax.servlet → 替换为:jakarta.servlet

  • 查找:javax.validation → 替换为:jakarta.validation

  • 查找:javax.annotation → 替换为:jakarta.annotation

3.2 关键变更点检查清单

// 1. JPA实体类
@Entity
@Table(name = "users")
public class User {
// @Id, @GeneratedValue 等注解现在来自jakarta包
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}

// 2. Controller中的校验注解
@RestController
public class UserController {
@PostMapping("/users")
public ResponseEntity<User> createUser(
@Valid @RequestBody UserDto userDto) { // @Valid现在来自jakarta
// …
}
}

// 3. 过滤器配置
@Component
public class AuthFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request, // jakarta.servlet.http.HttpServletRequest
HttpServletResponse response,
FilterChain filterChain) {
// …
}
}

3.3 自动化迁移脚本(可选)

对于大型项目,可以使用OpenRewrite自动迁移:

<!– 在pom.xml中添加 –>
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>5.40.0</version>
<configuration>
<activeRecipes>
<recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_2</recipe>
<recipe>org.openrewrite.java.migrate.jakarta.JavaxToJakarta</recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-spring</artifactId>
<version>5.7.0</version>
</dependency>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-migrate-java</artifactId>
<version>2.13.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

运行迁移:

mvn rewrite:run

第4步:Spring Security 5.x到6.x的迁移

4.1 配置类重大变更

// Spring Security 5.x的配置(需要修改)
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
}

// Spring Security 6.x的配置(新写法)
@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.httpBasic(Customizer.withDefaults());

return http.build();
}
}

4.2 关键API变更

Spring Security 5.xSpring Security 6.x迁移说明
antMatchers() requestMatchers() 方法名变更
mvcMatchers() requestMatchers() 统一使用新API
.and() Lambda DSL 不再需要链式调用
WebSecurityConfigurerAdapter SecurityFilterChain Bean 类已废弃

4.3 密码编码器变更

// 旧方式(Spring Security 5.x)
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

// 新方式(Spring Security 6.x)- 推荐使用DelegatingPasswordEncoder
@Bean
public PasswordEncoder passwordEncoder() {
String encodingId = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(encodingId, new BCryptPasswordEncoder());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());

return new DelegatingPasswordEncoder(encodingId, encoders);
}

第5步:数据库连接与JPA变更

5.1 数据源配置变更

# Spring Boot 2.7.x的配置(旧)
spring:
datasource:
url: jdbc:mysql://localhost:3306/db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
connection-timeout: 30000
maximum-pool-size: 10

# Spring Boot 3.2.x的配置(新)
spring:
datasource:
url: jdbc:mysql://localhost:3306/db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
connection-timeout: 30000
maximum-pool-size: 10
# 新增:SQL初始化模式
initialization-mode: always # 改为:spring.sql.init.mode=always

5.2 JPA配置变更

# 重要的配置变更
spring:
jpa:
# 旧配置(2.7.x)
# hibernate:
# ddl-auto: update
# naming:
# physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

# 新配置(3.2.x)
hibernate:
ddl-auto: update
# 命名策略配置方式变更
naming:
physical-strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy

# 新增:延迟加载配置
properties:
hibernate:
enable_lazy_load_no_trans: true
"[hibernate.format_sql]": true

# SQL初始化配置(从datasource中分离)
sql:
init:
mode: always
platform: mysql

5.3 数据库驱动版本升级

必须更新数据库驱动版本:

<!– MySQL驱动 –>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version> <!– 必须从8.0.x升级 –>
<scope>runtime</scope>
</dependency>

<!– PostgreSQL驱动 –>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.1</version> <!– 必须从42.5.x升级 –>
<scope>runtime</scope>
</dependency>

第6步:日志框架配置迁移

6.1 Logback配置变更

<!– logback-spring.xml 重要变更 –>
<configuration>
<!– 1. 移除过时的属性 –>
<!– 旧:<property name="LOG_PATH" value="./logs" /> –>
<!– 新: –>
<property name="LOG_PATH" value="${LOG_PATH:-./logs}" />

<!– 2. RollingFileAppender配置变更 –>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!– 最大文件大小配置变更 –>
<maxFileSize>100MB</maxFileSize> <!– 单位必须明确 –>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} – %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<!– 3. 异步日志配置 –>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>512</queueSize>
<discardingThreshold>0</discardingThreshold>
<appender-ref ref="FILE" />
<!– 新增:避免日志丢失的配置 –>
<neverBlock>true</neverBlock>
</appender>
</configuration>

6.2 Log4j2配置(如果使用)

<!– log4j2-spring.xml 配置示例 –>
<Configuration status="WARN" monitorInterval="30">
<Properties>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} – %msg%n</Property>
<Property name="LOG_PATH">./logs</Property>
</Properties>

<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>

<RollingFile name="File" fileName="${LOG_PATH}/app.log"
filePattern="${LOG_PATH}/app-%d{yyyy-MM-dd}-%i.log">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
</Appenders>

<Loggers>
<Logger name="com.yourpackage" level="debug" additivity="false">
<AppenderRef ref="File"/>
</Logger>

<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>

第7步:Web配置与Servlet API变更

7.1 WebMvcConfigurer变更

// Spring Boot 2.7.x(旧)
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST");
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor());
}
}

// Spring Boot 3.2.x(新)
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST")
.allowCredentials(true); // 新增:必须显式设置
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/static/**", "/error");
}
}

7.2 文件上传配置变更

# 文件上传配置变更
spring:
servlet:
multipart:
# 旧配置(2.7.x)
# max-file-size: 10MB
# max-request-size: 100MB

# 新配置(3.2.x)
max-file-size: 10MB
max-request-size: 100MB
# 新增:内存阈值配置
file-size-threshold: 0B
# 新增:清理临时文件
location: ${java.io.tmpdir}

# 新增:HTTP配置分离
web:
resources:
cache:
period: 3600

第8步:Actuator监控端点变更

8.1 端点配置变更

# Spring Boot 2.7.x Actuator配置
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always

# Spring Boot 3.2.x Actuator配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus # 新增prometheus
base-path: /actuator # 现在可以自定义
endpoint:
health:
show-details: always
# 新增:健康检查组配置
group:
custom:
include: diskSpace,ping,db
# 新增:度量的新配置
metrics:
export:
prometheus:
enabled: true
step: 1m

8.2 自定义健康检查

// Spring Boot 3.2.x的健康检查写法
@Component
public class CustomHealthIndicator implements HealthIndicator {

private final DataSource dataSource;

public CustomHealthIndicator(DataSource dataSource) {
this.dataSource = dataSource;
}

@Override
public Health health() {
// 检查数据库连接
try (Connection conn = dataSource.getConnection()) {
if (conn.isValid(1000)) {
return Health.up()
.withDetail("database", "available")
.withDetail("connection_time", "fast")
.build();
}
} catch (SQLException e) {
return Health.down(e)
.withDetail("database", "unavailable")
.withDetail("error", e.getMessage())
.build();
}

return Health.unknown().build();
}
}

第9步:第三方依赖兼容性处理

9.1 常见依赖升级指南

<!– 1. MyBatis升级 –>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version> <!– 从2.3.x升级 –>
</dependency>

<!– 2. 如果使用Druid连接池 –>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.20</version> <!– 注意:必须是3.x的starter –>
</dependency>

<!– 3. 如果使用Redis –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!– 注意:需要显式添加commons-pool2 –>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.12.0</version>
</dependency>

<!– 4. 如果使用Swagger/SpringDoc –>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version> <!– 替代springfox –>
</dependency>

9.2 必须检查的依赖

运行以下命令检查不兼容的依赖:

# 查找所有javax依赖
mvn dependency:tree | grep javax

# 查找所有可能不兼容的依赖
mvn dependency:tree | grep -E "(servlet|persistence|validation|annotation)" | grep -v jakarta

# 使用Maven Enforcer插件检查
mvn enforcer:enforce -Drules=dependencyConvergence

第10步:测试与验证

10.1 分阶段验证策略

第一阶段:编译验证

# 清理并编译
mvn clean compile

# 检查是否有编译错误
if [ $? -eq 0 ]; then
echo "✅ 编译通过"
else
echo "❌ 编译失败,请检查错误信息"
exit 1
fi

第二阶段:单元测试

# 运行所有单元测试
mvn test -DskipTests=false

# 检查测试通过率
TEST_RESULT=$(mvn test -DskipTests=false 2>&1 | grep "Tests run:")

echo "测试结果:$TEST_RESULT"

第三阶段:集成测试

# 使用Testcontainers运行集成测试
mvn verify -DskipITs=false

# 或者手动启动应用测试
mvn spring-boot:run &
APP_PID=$!

# 等待应用启动
sleep 30

# 测试关键端点
curl -f http://localhost:8080/actuator/health || echo "健康检查失败"
curl -f http://localhost:8080/api/test || echo "API测试失败"

# 停止应用
kill $APP_PID

10.2 性能对比测试

创建性能测试脚本:

#!/bin/bash
# 性能对比测试脚本

echo "=== Spring Boot 2.7.x 性能测试 ==="
# 启动旧版本应用
# 运行压测…

echo "=== Spring Boot 3.2.x 性能测试 ==="
# 启动新版本应用
# 运行同样压测…

# 比较结果
echo "性能提升:"
echo "- 启动时间:减少约30%"
echo "- 内存占用:减少约20%"
echo "- 吞吐量:提升约15%"

10.3 回滚计划

如果升级过程中遇到问题,需要准备回滚方案:

# 1. 代码回滚
git reset –hard HEAD~1

# 2. 数据库回滚(如果执行了迁移脚本)
# 备份恢复…

# 3. 配置文件恢复
cp application-backup.yml application.yml

# 4. 依赖恢复
mvn clean install -U

升级后优化建议

1. 启用新特性

// 1. 启用虚拟线程(需要JDK 21+)
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}

// 2. 使用新的RestClient(替代RestTemplate)
@Bean
public RestClient restClient() {
return RestClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader("User-Agent", "SpringBootApp")
.build();
}

// 3. 响应式编程(如果适用)
@Bean
public RouterFunction<ServerResponse> routes() {
return RouterFunctions.route()
.GET("/users", request -> ServerResponse.ok().bodyValue(userService.findAll()))
.build();
}

2. 监控与告警配置

# 新增:Micrometer监控配置
management:
metrics:
export:
prometheus:
enabled: true
datadog:
enabled: false
newrelic:
enabled: false
distribution:
percentiles-histogram:
"[http.server.requests]": true
tags:
application: ${spring.application.name}
environment: ${ENV:local}

tracing:
sampling:
probability: 1.0

常见问题与解决方案

Q1:启动时出现ClassNotFoundException: javax.servlet.Filter

原因:没有正确迁移到jakarta 解决:

  • 检查所有import语句

  • 确保依赖中没有旧的servlet-api

  • 运行mvn dependency:tree | grep servlet检查

  • Q2:Hibernate无法启动,报Table not found

    原因:DDL自动生成策略变化 解决:

    spring:
    jpa:
    hibernate:
    ddl-auto: validate # 先使用validate模式
    properties:
    hibernate:
    "[hibernate.hbm2ddl.auto]": validate

    Q3:Spring Security配置不生效

    原因:SecurityFilterChain配置错误 解决:

    @Bean
    @Order(1) // 确保顺序正确
    public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
    http
    .securityMatcher("/api/**") // 明确指定匹配路径
    .authorizeHttpRequests(auth -> auth
    .anyRequest().authenticated()
    )
    .httpBasic(Customizer.withDefaults());
    return http.build();
    }

    Q4:性能下降或内存泄漏

    排查步骤:

  • 使用jcmd <pid> VM.native_memory分析内存

  • 检查GC日志:-Xlog:gc*:file=gc.log

  • 使用AsyncProfiler进行性能分析

  • 总结

    从Spring Boot 2.7升级到3.2是一个系统性的工程,但收益显著。按照这10个步骤,你可以:

  • 安全地完成升级,避免生产事故

  • 充分利用新特性,提升应用性能

  • 保持技术栈先进性,为未来升级奠定基础

  • 最后建议:

    • 在测试环境充分验证至少1周

    • 逐步灰度发布到生产环境

    • 建立监控告警,关注关键指标

    升级成功标志: ✅ 应用启动时间减少30%以上 ✅ 内存占用降低20%以上 ✅ 所有测试用例通过 ✅ 生产环境运行稳定24小时+

    记住:一次成功的升级,不仅是为了使用新版本,更是为了构建更稳定、高效的技术基础。


    升级支持:如果在升级过程中遇到问题,可以在评论区留言,我会提供针对性的解决方案。

    资源推荐:

    • Spring Boot 3.2官方迁移指南

    • OpenRewrite自动化迁移工具

    • Spring Boot升级检查清单

    赞(0)
    未经允许不得转载:171主机测评 » Spring Boot 3.2升级实战:从2.7平稳迁移的10个关键步骤
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址