spring-boot-2.7-upgrade-guide.md 20 KB

Spring Boot 2.2.13 → 2.7.18 升级指南

本文档基于 ylrz_his_scrm_java 项目现状编写,目标版本:Spring Boot 2.7.18(2.x 末代 LTS,仍支持 Java 8)。


一、升级概览

1.1 当前 vs 目标

当前 升级后(Boot 2.7.18 BOM 默认)
Spring Boot 2.2.13.RELEASE 2.7.18
Spring Framework 5.2.x 5.3.31
Spring Security 5.2.15(手动覆盖) 5.7.11(随 BOM,可不再 pin)
Java 1.8 1.8(不变)
Servlet API javax.* javax.*(不涉及 Jakarta 迁移

1.2 项目规模(影响工作量)

维度 数量
Java 源文件 ~8500
Maven 模块 36+
可运行 Spring Boot 应用 28
SecurityConfig(需迁移) 26
SwaggerConfig(Springfox) 23
HandlerInterceptorAdapter(已废弃) 37
共享配置文件 fs-service/src/main/resources/application-common.yml

1.3 预估工期

阶段 内容 预估
阶段 0 准备与分支 0.5 天
阶段 1 POM / 依赖 1–2 天
阶段 2 全局运行时配置 0.5 天
阶段 3 Spring Security 迁移 3–5 天
阶段 4 Swagger 兼容 1–3 天
阶段 5 编译修复 & 启动 2–3 天
阶段 6 回归测试 1–2 周

合计:约 2–4 周(1 人全职,视测试深度而定)。


二、升级前准备(阶段 0)

2.1 创建升级分支

git checkout -b feature/spring-boot-2.7.18

2.2 建立基线

  • 记录当前各核心模块能否正常 mvn package、能否启动。
  • 导出当前依赖树备查:
mvn dependency:tree -pl fs-admin -Dverbose > docs/upgrade-baseline-admin-deps.txt
mvn dependency:tree -pl fs-user-app -Dverbose > docs/upgrade-baseline-user-app-deps.txt

2.3 确定升级范围

建议一次性升 BOM,不要停留在 2.3/2.5 中间版本——你们从 2.2 直接到 2.7,中间破坏性变更(路径匹配、循环依赖、Security API)集中处理更高效。

2.4 升级顺序建议

fs-common / fs-framework(基础)
    ↓
fs-service(业务核心)
    ↓
fs-admin / fs-user-app / fs-company(主入口)
    ↓
其余 API / 任务 / MQ 模块

三、依赖与 POM 升级(阶段 1)

3.1 修改根 pom.xml

3.1.1 升级 Spring Boot BOM

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.7.18</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

3.1.2 新增/调整 properties(建议值)

<properties>
    <!-- 核心 -->
    <spring-boot.version>2.7.18</spring-boot.version>

    <!-- 第三方 starter(与 Boot 2.7 对齐) -->
    <mybatis-spring-boot.version>2.3.2</mybatis-spring-boot.version>
    <druid.version>1.2.23</druid.version>

    <!-- Redisson:Boot 2.7 对应 spring-data-27 -->
    <redisson.version>3.27.2</redisson.version>

    <!-- MyBatis-Plus:当前 3.1.0 过旧,建议升级 -->
    <mybatis-plus.version>3.5.5</mybatis-plus.version>
</properties>

3.1.3 移除或下调的手动覆盖

升级到 2.7.18 后,以下覆盖可先删除,改由 BOM 管理;删除后跑 dependency:tree 确认无 CVE 回退:

属性 / 依赖 当前 建议
spring-security.version 及 core/web/config 三件套 5.2.15 手动 pin 删除,使用 BOM 5.7.x
jackson.version / jackson-bom 2.12.7 手动 pin 删除或改为仅覆盖个别 artifact
logback.version 1.2.13 Boot 2.7 自带 1.2.12+,可删除
snakeyaml.version 1.33 Boot 2.7 自带 1.30+,可删除

保留(仍可能有传递依赖冲突):

  • netty.versionhutool.versionokhttp.versionfastjson.version 等安全相关 pin
  • pagehelper(你们有 SQL 注入 CVE 注释,需验证 BOM 版本是否 ≥ 5.3.1)

3.1.4 Redisson 适配(必改)

pom.xmlfs-service/pom.xml 中:

<!-- 改前 -->
<artifactId>redisson-spring-data-22</artifactId>

<!-- 改后 -->
<artifactId>redisson-spring-data-27</artifactId>

fs-service/pom.xml 中排除 redisson-spring-data-32 的注释也需同步更新。

3.1.5 统一 spring-boot-maven-plugin

当前各可运行模块 pin 了 2.1.1.RELEASE(如 fs-admin/pom.xml),建议:

方案 A(推荐):在根 pom.xmlpluginManagement 中统一管理:

<pluginManagement>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>${spring-boot.version}</version>
        </plugin>
    </plugins>
</pluginManagement>

子模块删除 <version>2.1.1.RELEASE</version>

涉及模块(27 个):fs-admin、fs-user-app、fs-company、fs-company-app、fs-doctor-app、fs-store、fs-qw-*、fs-live-*、fs-ad-、fs-wx-api、fs-watch、fs-redis、fs-websocket、fs-ai-chat、fs-common-api、fs-repeat-api、fs-ipad-task、fs-qwhook 等。

3.2 修改 fs-common/pom.xml

3.2.1 MyBatis-Plus

<!-- 改前 -->
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>

<!-- 改后 -->
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>  <!-- 3.5.5 -->

3.2.2 spring-expression 版本冲突(重要)

当前 fs-common/pom.xml 显式依赖了 spring-expression 6.2.0(Spring 6),与 Boot 2.7 的 Spring 5.3 不兼容

<!-- 建议:删除该依赖,或改为不指定 version,由 BOM 管理 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <!-- 删除 <version>6.2.0</version> -->
</dependency>

3.3 MySQL 驱动(可选)

Boot 2.7 仍支持 mysql-connector-java,但官方已迁移到 com.mysql:mysql-connector-j。短期可不改;长期建议替换:

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>

3.4 验证命令

# 全量编译(先不要求启动)
mvn clean compile -DskipTests

# 检查冲突
mvn dependency:tree -pl fs-admin | findstr /i "spring-security spring-framework spring-expression"

四、全局运行时配置(阶段 2)

fs-service/src/main/resources/application-common.ymlspring: 节点下增加:

spring:
  # Boot 2.6+ 默认禁止循环依赖,老项目常见此问题
  main:
    allow-circular-references: true   # 临时方案;启动稳定后逐步改为 false 并重构

  # Boot 2.6+ 默认 PathPatternParser,与 Springfox 2.9.2 / 部分 antMatchers 不兼容
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher   # 短期保留 Springfox 时使用

说明ant_path_matcher 在 2.7 仍可用但已标记为过渡方案。若后续迁移到 springdoc-openapi,可移除此配置。

4.1 Tomcat 配置项更名(如遇警告)

Boot 2.4+ 部分 server.tomcat.* 属性已更名,你们当前使用:

server:
  tomcat:
    max-threads: 800        # 建议改为 max-threads 或 threads.max
    min-spare-threads: 30   # 建议改为 threads.min-spare

启动时若出现 deprecated 警告,按 Boot 2.7 文档调整即可,不影响功能。


五、Spring Security 迁移(阶段 3,工作量最大)

5.1 背景

  • Boot 2.7 自带 Spring Security 5.7.x
  • WebSecurityConfigurerAdapter 已在 5.7 移除
  • 项目共 26 个 SecurityConfig 需迁移

5.2 配置文件分类

A 类:全放行 + StrictHttpFirewall(17 个,改动模式相同)

适用模块:fs-user-app、fs-doctor-app、fs-live-app、fs-live-mq、fs-watch、fs-redis、fs-wx-api、fs-ad-api、fs-ad-new-api、fs-common-api、fs-repeat-api、fs-qwhook、fs-qwhook-msg、fs-qwhook-sop、fs-qw-mq、fs-qw-voice、fs-user-app-ai-chat、fs-company-app

参考现有代码:fs-user-app/.../SecurityConfig.java

B 类:JWT 完整鉴权(7 个,规则较多)

模块 文件 特殊点
fs-framework fs-framework/.../SecurityConfig.java fs-admin 依赖此模块,规则最全
fs-company fs-company/.../SecurityConfig.java 使用 MD5PasswordEncoder
fs-store fs-store/.../SecurityConfig.java 使用 MD5PasswordEncoder
fs-qw-api fs-qw-api/.../SecurityConfig.java JWT + 白名单
fs-qw-api-msg fs-qw-api-msg/.../SecurityConfig.java JWT + 白名单
fs-qw-company-api fs-qw-company-api/.../SecurityConfig.java JWT + 白名单
fs-qw-task fs-qw-task/.../SecurityConfig.java JWT + 白名单
fs-ipad-task fs-ipad-task/.../SecurityConfig.java JWT + 白名单

fs-admin 无独立 SecurityConfig,通过 fs-framework 生效。

5.3 推荐重构策略(减少重复劳动)

不要 26 个文件各改一遍。 建议:

  1. fs-framework 新增 2 个基类/工具:
    • PermitAllSecurityConfiguration(A 类)
    • JwtSecurityConfigurationSupport(B 类,抽取 filter 链构建)
  2. 各模块 SecurityConfig 改为 @Import@Configuration + @Bean SecurityFilterChain
  3. 模块级差异(白名单 URL)通过 @ConfigurationProperties 或子类 override 注入

5.4 迁移模板

A 类:全放行(替代 fs-user-app 现有写法)

@Configuration
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                .anyRequest().permitAll()
            );
        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }

    @Bean
    public StrictHttpFirewall httpFirewall() {
        StrictHttpFirewall firewall = new StrictHttpFirewall();
        firewall.setAllowUrlEncodedDoubleSlash(true);
        return firewall;
    }
}

B 类:JWT 鉴权(替代 fs-framework 现有写法)

@Configuration
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig {

    @Autowired private UserDetailsService userDetailsService;
    @Autowired private AuthenticationEntryPointImpl unauthorizedHandler;
    @Autowired private LogoutSuccessHandlerImpl logoutSuccessHandler;
    @Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter;
    @Autowired private CorsFilter corsFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .exceptionHandling(ex -> ex.authenticationEntryPoint(unauthorizedHandler))
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/register", "/captchaImage").anonymous()
                .requestMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
                // ... 保留原有全部 antMatchers 路径 ...
                .anyRequest().authenticated()
            )
            .headers(headers -> headers.frameOptions(frame -> frame.disable()))
            .logout(logout -> logout
                .logoutUrl("/logout")
                .logoutSuccessHandler(logoutSuccessHandler)
            );

        http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
        http.addFilterBefore(corsFilter, LogoutFilter.class);

        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
}

API 对照表

旧 API(5.2 / WebSecurityConfigurerAdapter) 新 API(5.7 / SecurityFilterChain)
extends WebSecurityConfigurerAdapter @Bean SecurityFilterChain
@EnableGlobalMethodSecurity @EnableMethodSecurity
configure(HttpSecurity http) securityFilterChain(HttpSecurity http)
authorizeRequests() authorizeHttpRequests()
antMatchers(...) requestMatchers(...)
authenticationManagerBean() AuthenticationConfiguration.getAuthenticationManager()
.csrf().disable() .csrf(csrf -> csrf.disable())

5.5 处理 fs-common 中的 Security 补丁

升级后 Spring Security 升至 5.7.x,需重新评估以下类:

文件 用途 建议
EagerSecurityHeadersBeanPostProcessor CVE-2026-22732 缓解 5.7 有官方修复,验证后可删除
TimingSafeAuthenticationManagerBeanPostProcessor CVE-2026-22746 缓解 5.7.23+ 有修复;先保留,官方版本覆盖后删除
TimingSafeDaoAuthenticationProvider 同上 同上
org.springframework.security.* 包下自研类 覆盖框架类 升级后重点回归登录接口

注意:不要同时保留自研 TimingSafeDaoAuthenticationProvider 与官方修复版行为,避免双重逻辑。

5.6 Security 迁移检查清单

  • 26 个 SecurityConfig 编译通过
  • fs-admin 登录 / 登出 / JWT 刷新
  • fs-user-app 接口鉴权(当前全 permitAll,确认是否符合预期)
  • fs-company / fs-store 的 MD5 密码编码仍生效
  • 各模块 Swagger 白名单路径仍可匿名访问
  • @PreAuthorize / @Secured 注解权限仍生效

六、Swagger / Springfox 兼容(阶段 4)

6.1 现状

  • 使用 Springfox 2.9.2swagger.version
  • 23 个 SwaggerConfig + 多个 ResourcesConfig 注册 Swagger 静态资源
  • 共享配置 application-common.ymlswagger.enabled: false(生产默认关闭)

6.2 短期方案(推荐先做,保证升级后能启动)

  1. 阶段 2 已配置 spring.mvc.pathmatch.matching-strategy: ant_path_matcher
  2. 保持 Springfox 2.9.2 不变
  3. 启动后访问 /swagger-ui.html 验证

已知风险:Springfox 已停止维护,与 Boot 2.7 是「勉强兼容」,不是长期方案。

6.3 长期方案(可选,单独排期)

迁移到 springdoc-openapi 1.7.x(支持 Boot 2.7 + Java 8):

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.7.0</version>
</dependency>
  • 删除 Springfox 依赖
  • 23 个 SwaggerConfig 合并为 1 个公共配置或删除(springdoc 自动扫描)
  • Controller 上 @Api@Tag@ApiOperation@Operation(可渐进)

6.4 SwaggerConfig 文件清单

fs-admin/src/main/java/com/fs/web/core/config/SwaggerConfig.java
fs-user-app/.../SwaggerConfig.java
fs-company-app/.../core/config/SwaggerConfig.java
fs-doctor-app、fs-live-app、fs-live-mq、fs-watch、fs-redis、
fs-wx-api、fs-ad-api、fs-common-api、fs-repeat-api、
fs-qw-api、fs-qw-api-msg、fs-qw-company-api、fs-qw-task、
fs-qw-mq、fs-qw-voice、fs-ipad-task、
fs-qwhook、fs-qwhook-msg、fs-qwhook-sop、
fs-user-app-ai-chat

七、其他代码调整(阶段 5)

7.1 HandlerInterceptorAdapter(37 处,低优先级)

多个模块的 RepeatSubmitInterceptorAuthorizationInterceptor 继承了 HandlerInterceptorAdapter

  • Boot 2.7 下仍可编译运行(deprecated)
  • 建议后续改为直接实现 HandlerInterceptor 接口,删除 extends HandlerInterceptorAdapter

7.2 MyBatis / MyBatis-Plus

  • 升级 MyBatis-Plus 3.1.0 → 3.5.5 后,关注:
    • fieldStrategy: NOT_EMPTY 等全局配置是否仍兼容(3.5 部分枚举有变更)
    • 分页插件、乐观锁、多租户等自定义配置
  • 23 个 MyBatisConfig.java 若仅做别名扫描,一般无需改动

7.3 业务代码

  • 无需 Jakarta 包名替换javax.servlet 等保持不变)
  • fs-service 等业务 Service / Controller 预计改动极少
  • 重点回归:支付、订单、企微回调、直播、课程观看、Redis 锁

八、分模块启动验证(阶段 5–6)

8.1 启动顺序

优先级 模块 验证重点
P0 fs-admin 后台登录、菜单权限、Swagger
P0 fs-user-app C 端 API、微信支付、订单
P0 fs-company 企业端 JWT、CRM
P1 fs-qw-api / fs-qw-task 企微回调、定时任务
P1 fs-live-app / fs-live-mq 直播、MQ 消费
P2 fs-qwhook* / fs-wx-api / fs-ad-* 回调、广告
P2 其余模块 能启动、核心接口 200

8.2 单模块启动示例

cd fs-admin
mvn spring-boot:run -Dspring-boot.run.profiles=dev

8.3 常见问题排查

现象 可能原因 处理
启动报循环依赖 Boot 2.6+ 默认禁止 allow-circular-references: true
Swagger 404 / 空文档 PathPattern 不兼容 ant_path_matcher
403 增多 Security 规则迁移遗漏 对比旧 antMatchers 列表
Redisson 启动失败 spring-data 版本不匹配 改用 redisson-spring-data-27
NoClassDefFoundError: spring-expression fs-common pin 了 Spring 6 删除 version 6.2.0
静态资源 404 ResourcesConfig 路径匹配变化 检查 ResourcesConfig

九、回归测试清单(阶段 6)

9.1 基础设施

  • 多数据源 / Druid 连接池正常
  • Redis / Redisson 分布式锁
  • MyBatis / MyBatis-Plus CRUD、分页
  • 定时任务(fs-quartz)
  • WebSocket(fs-websocket)

9.2 安全

  • 各端登录(admin / user / company / doctor)
  • JWT 过期与刷新
  • 权限注解 @PreAuthorize
  • 匿名白名单(支付回调、企微回调、物流回调)

9.3 业务链路

  • 下单 → 支付 → 回调
  • 售后 / 退款
  • 课程观看 / 完播任务(fs-qw-task)
  • 企微 SOP / 消息推送
  • 直播下单 / 购物车
  • 文件上传 OSS

十、推荐执行步骤汇总

阶段 0  建分支、打基线、确认回滚方案
   ↓
阶段 1  升 BOM 2.7.18 → Redisson 27 → MyBatis-Plus → 删 spring-expression 6.2.0
   ↓        统一 spring-boot-maven-plugin
   ↓
阶段 2  application-common.yml 加 circular-ref + ant_path_matcher
   ↓
阶段 3  重构 SecurityConfig(先 fs-framework,再各模块)
   ↓        评估删除 fs-common Security 补丁
   ↓
阶段 4  验证 Springfox(或排期 springdoc)
   ↓
阶段 5  mvn compile → 按 P0→P2 逐个启动修错
   ↓
阶段 6  全链路回归 → 预发验证 → 生产灰度

十一、不在本次范围内

以下内容属于 Spring Boot 3.x 范畴,本次 2.7.18 不需要做

  • Java 17+ 升级
  • javax.*jakarta.* 包名替换
  • Spring Security 6.x API
  • Redisson spring-data-32

十二、参考链接


文档生成日期:2026-06-08