Spring Boot 迁移排查指南

Spring Boot 权限框架迁移排查指南

当你更换权限管理系统,引入新依赖后遇到 Bean 找不到的错误时,按照本文档逐步排查。


目录


一、常见错误类型速查表

错误类型 典型报错信息 可能原因 解决方案
Bean 不存在 required a bean of type 'xxx' that could not be found 依赖未引入、自动配置未生效、包扫描遗漏 见步骤 1-5
依赖冲突 NoSuchMethodErrorNoClassDefFoundError 新旧框架版本冲突、传递依赖覆盖 见步骤 2-3
自动配置未生效 启动正常但功能不工作 条件注解不满足、被排除配置 见步骤 4
循环依赖 BeanCurrentlyInCreationException Bean 相互依赖 见步骤 7
配置缺失 Could not resolve placeholder yml/properties 配置不完整 见步骤 6
类冲突 ClassNotFoundException 旧框架类残留、类加载问题 见步骤 3

二、排查流程总览

┌─────────────────────────────────────────────────────────────┐
│                    Bean 找不到排查流程                        │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │ 1. 确认缺哪个Bean │
                    └────────┬────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │ 2. 新依赖存在吗? │──── 否 ──→ 检查 pom.xml / 私服配置
                    └────────┬────────┘
                              │ 是
                              ▼
                    ┌─────────────────┐
                    │ 3. 旧依赖清干净? │──── 否 ──→ 删除旧依赖、排除传递依赖
                    └────────┬────────┘
                              │ 是
                              ▼
                    ┌─────────────────┐
                    │ 4. 自动配置生效? │──── 否 ──→ 检查条件注解、手动启用
                    └────────┬────────┘
                              │ 是
                              ▼
                    ┌─────────────────┐
                    │ 5. 包扫描覆盖?   │──── 否 ──→ 添加 @ComponentScan
                    └────────┬────────┘
                              │ 是
                              ▼
                    ┌─────────────────┐
                    │ 6. 框架特殊要求? │──── 检查文档、添加必要配置
                    └────────┬────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │ 7. 循环依赖?     │──── 使用 @Lazy 或重构
                    └────────┬────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │ 8. 最小示例验证   │
                    └─────────────────┘

三、详细排查步骤

步骤 1:确认缺失的 Bean

目的:明确问题是什么,不要猜测。

操作方法

  1. 找到完整的错误堆栈(控制台输出或 logs/ 目录下的日志文件)

  2. 定位关键信息,典型格式如下:

    ***************************
    APPLICATION FAILED TO START
    ***************************
    
    Description:
    A component required a bean of type 'com.xxx.SomeService' that could not be found.
    
    Action:
    Consider defining a bean of type 'com.xxx.SomeService' in your configuration.
    
  3. 记录以下信息

    • 缺失 Bean 的全限定类名(如 com.xxx.SomeService
    • 是哪个类在注入它(搜索 @Autowired@Resource 引用该类的地方)
    • 报错的完整堆栈路径
  4. 如果日志被截断,临时开启 DEBUG 日志:

    # application.yml
    logging:
      level:
        root: DEBUG
    

    或启动时加参数:

    java -jar app.jar --logging.level.root=DEBUG
    

步骤 2:确认新依赖引入成功

目的:验证新框架的 jar 包是否真的在 classpath 中。

Maven 项目

# 查看依赖树,过滤新框架
mvn dependency:tree | grep "新框架的groupId"

# 如果怀疑版本冲突,查看完整依赖树
mvn dependency:tree -Dverbose

# 查看有效 POM(检查 dependencyManagement 是否覆盖了版本)
mvn help:effective-pom

Gradle 项目

# 查看依赖
gradle dependencies | grep "新框架的groupId"

# 查看特定配置的依赖
gradle dependencies --configuration runtimeClasspath

常见问题

问题 现象 解决方案
依赖不存在 dependency:tree 找不到 检查 pom.xml 中的 groupId、artifactId、version 是否正确
版本不对 显示的版本不是预期的 检查 <dependencyManagement> 或父 POM 是否覆盖了版本
私服问题 下载失败 检查 settings.xml 中的私服地址和认证信息
传递依赖缺失 新框架依赖的其他包没拉下来 检查是否需要额外添加依赖,或私服上是否有这些包

步骤 3:清除旧依赖残留

目的:旧框架的残留会导致类冲突、自动配置冲突。

这是最常见的坑!

3.1 全局搜索旧框架引用

# 在项目根目录执行(需要安装 ripgrep,或用 grep 替代)
rg "旧框架的groupId" --type xml --type java --type yml --type properties

# 或者用 find + grep
find . -type f \( -name "*.xml" -o -name "*.java" -o -name "*.yml" -o -name "*.properties" \) -exec grep -l "旧框架关键字" {} \;

需要检查的位置

  • pom.xml / pom.xml 中的 <modules>
  • build.gradle / build.gradle.kts
  • application.yml / application.properties
  • application-{profile}.yml 等环境配置文件
  • 所有 @Import@ComponentScan@EnableXxx 注解
  • src/main/resources/META-INF/spring.factories
  • src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

3.2 检查传递依赖

旧框架可能被其他依赖间接引入:

# Maven 查看谁引入了旧框架
mvn dependency:tree | grep -B 5 "旧框架的artifactId"

# Gradle 查看依赖来源
gradle dependencies --configuration runtimeClasspath | grep -B 5 "旧框架"

如果发现是传递依赖,在引入该依赖的地方排除:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>some-library</artifactId>
    <exclusions>
        <exclusion>
            <groupId>旧框架的groupId</groupId>
            <artifactId>旧框架的artifactId</artifactId>
        </exclusion>
    </exclusions>
</dependency>

3.3 清理构建缓存

# Maven
mvn clean

# Gradle
gradle clean

# 如果还不行,删除本地仓库缓存(谨慎操作)
rm -rf ~/.m2/repository/旧框架的groupId路径

步骤 4:检查自动配置是否生效

目的:Spring Boot 的自动配置可能因为条件不满足而没有创建 Bean。

4.1 开启自动配置报告

# application.yml
debug: true

或启动参数:

java -jar app.jar --debug

4.2 分析报告

启动后控制台会打印:

============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:(自动配置生效的)
-----------------
   XxxAutoConfiguration matched:
      - @ConditionalOnClass found required class (OnClassCondition)

Negative matches:(自动配置未生效的)
-----------------
   XxxAutoConfiguration:
      Did not match:
         - @ConditionalOnMissingClass found unwanted class (OnClassCondition)
         - @ConditionalOnProperty did not find property (OnPropertyCondition)

重点关注

  • 新框架的自动配置类在 Positive 还是 Negative
  • 如果在 Negative,看具体原因:
    • @ConditionalOnClass → 缺少某个类
    • @ConditionalOnMissingClass → 存在冲突的类
    • @ConditionalOnProperty → 缺少配置属性
    • @ConditionalOnBean → 缺少某个 Bean
    • @ConditionalOnMissingBean → 已存在同名 Bean 冲突

4.3 手动启用自动配置

如果自动配置被意外排除,可以手动导入:

@SpringBootApplication
@Import({新框架的AutoConfiguration类.class})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

4.4 检查排除配置

查看启动类或配置类是否有排除:

// 检查是否有这种写法
@SpringBootApplication(exclude = {XxxAutoConfiguration.class})
// 或
@EnableAutoConfiguration(exclude = {XxxAutoConfiguration.class})

步骤 5:检查包扫描路径

目的:Spring 只会扫描启动类所在包及其子包。

5.1 确认扫描范围

假设你的启动类在 com.company.app.Application,默认扫描范围是 com.company.app 及其子包。

如果新框架的 Bean 在 com.newframework 下,就不会被扫描到。

5.2 扩展扫描范围

@SpringBootApplication
@ComponentScan(basePackages = {
    "com.company.app",    // 你的项目包
    "com.newframework"    // 新框架的包
})
public class Application { }

或使用 @Import

@SpringBootApplication
@Import({新框架的配置类.class})
public class Application { }

步骤 6:检查框架特殊配置要求

目的:有些框架需要额外的配置才能工作。

6.1 查看官方文档

找到新框架的:

  • GitHub README
  • 官方文档的 Quick Start / Getting Started
  • 示例项目

6.2 常见需要手动配置的情况

框架类型 可能需要的配置
Spring Security @EnableWebSecuritySecurityFilterChain Bean、UserDetailsService Bean
Shiro ShiroFilterFactoryBeanSecurityManager、Realm 配置
Sa-Token StpUtil 配置、权限接口实现
自研框架 可能需要 @EnableXxx 注解、配置类、特定 Bean

6.3 检查配置属性

新框架可能要求在 yml 中配置:

# 示例:检查是否需要这类配置
newframework:
  enabled: true
  some-property: value

6.4 查看自动配置源码

如果文档不清楚,直接看 jar 包里的自动配置类:

# 解压 jar 包
jar -xf 新框架.jar

# 查看自动配置
cat META-INF/spring.factories
cat META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

# 反编译自动配置类,看它需要什么条件

步骤 7:排查循环依赖

目的:Bean 创建顺序问题可能导致"找不到"。

7.1 识别循环依赖

报错信息示例:

The dependencies of some of the beans in the application context form a cycle:
   ┌─────┐
   |  serviceA defined in file [...]
   ↑     ↓
   |  serviceB defined in file [...]
   └─────┘

或:

BeanCurrentlyInCreationException: Error creating bean with name 'xxx': 
Requested bean is currently in creation: Is there an unresolvable circular reference?

7.2 解决方案

方案一:使用 @Lazy 延迟注入

@Service
public class ServiceA {
    private final ServiceB serviceB;
    
    public ServiceA(@Lazy ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

方案二:使用 Setter 注入代替构造器注入

@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

方案三:重构代码,消除循环依赖

将公共逻辑抽取到第三个 Service 中。

7.3 Spring Boot 2.6+ 注意

Spring Boot 2.6+ 默认禁止循环依赖,如果必须使用,需要配置:

spring:
  main:
    allow-circular-references: true

步骤 8:最小可运行示例验证

目的:排除其他干扰,确认新框架本身能工作。

8.1 创建空项目

使用 Spring Initializr 或 IDE 创建一个新的 Spring Boot 项目,只包含:

  • Web 依赖
  • 新权限框架依赖

8.2 最简配置

只添加新框架要求的最基本配置,启动项目。

8.3 逐步迁移

如果空项目能启动,逐步把老项目的代码搬过来:

空项目(能启动)
    ↓ 添加一个模块
模块1(能启动)
    ↓ 添加另一个模块
模块1+2(能启动)
    ↓ 继续添加
...
    ↓ 添加到某个模块时失败
定位问题模块

四、常见问题与解决方案

问题 1:NoSuchMethodError / NoClassDefFoundError

原因:新旧框架的类或方法签名冲突。

排查

# 查找冲突的类
mvn dependency:tree -Dverbose | grep "冲突的类名"

# 或使用 arthas 在运行时查看类来源
jad 全限定类名

解决

  • 排除冲突的传递依赖
  • 统一版本管理

问题 2:配置属性不生效

原因:配置前缀错误、配置文件未加载、profile 配置覆盖。

排查

# 临时开启配置绑定调试
logging:
  level:
    org.springframework.boot.context.properties: DEBUG

解决

  • 检查 @ConfigurationProperties 的 prefix
  • 确认配置文件名和位置正确
  • 检查是否有 profile 覆盖(如 application-prod.yml

问题 3:Bean 被覆盖或重复

原因:新旧框架定义了同名 Bean。

报错示例

The bean 'xxxService', defined in class path resource [...], 
could not be registered. A bean with that name has already been 
defined in class path resource [...].

解决

# 允许 Bean 覆盖(不推荐,只是临时方案)
spring:
  main:
    allow-bean-definition-overriding: true

更好的方案:找到重复定义的 Bean,删除或重命名其中一个。


问题 4:条件注解不满足

原因:自动配置类的条件注解要求未满足。

常见条件注解

注解 含义 不满足时的解决
@ConditionalOnClass classpath 中存在某类 添加对应依赖
@ConditionalOnMissingClass classpath 中不存在某类 移除冲突依赖
@ConditionalOnBean 容器中存在某 Bean 创建对应 Bean
@ConditionalOnMissingBean 容器中不存在某 Bean 移除冲突 Bean
@ConditionalOnProperty 配置属性满足条件 添加对应配置
@ConditionalOnWebApplication 是 Web 应用 确保 Web 环境

问题 5:类加载问题

原因:多模块项目中,类加载顺序或范围问题。

排查

# 查看运行时类路径
java -verbose:class -jar app.jar | grep "类名"

# 或在代码中打印
System.out.println(getClass().getClassLoader().getResource("类路径"));

解决

  • 检查多模块依赖关系
  • 确保没有重复的 jar 包

五、排查清单

将以下清单复制出来,逐项检查:

□ 1. 确认缺失的 Bean
   □ 记录 Bean 全限定类名
   □ 记录注入位置
   □ 记录完整报错堆栈

□ 2. 确认新依赖
   □ dependency:tree 能找到新框架
   □ 版本正确
   □ 传递依赖完整

□ 3. 清除旧依赖
   □ pom.xml / build.gradle 中删除
   □ 全局搜索旧框架关键字,无残留
   □ 排除传递依赖中的旧框架
   □ 清理 spring.factories / AutoConfiguration.imports
   □ 执行 mvn clean / gradle clean

□ 4. 自动配置
   □ 开启 debug 模式
   □ 新框架在 Positive matches 中
   □ 检查排除配置

□ 5. 包扫描
   □ 新框架包在扫描范围内
   □ 或使用 @Import 手动导入

□ 6. 框架特殊要求
   □ 查看官方文档
   □ 添加必要的 @EnableXxx 注解
   □ 添加必要的配置属性
   □ 创建必要的 Bean

□ 7. 循环依赖
   □ 无循环依赖报错
   □ 必要时使用 @Lazy

□ 8. 最小示例
   □ 空项目能启动
   □ 逐步迁移定位问题模块

附录:常用命令速查

# Maven
mvn dependency:tree                    # 查看依赖树
mvn dependency:tree -Dverbose          # 详细依赖树(含冲突)
mvn dependency:tree -Dincludes=groupId:artifactId  # 过滤特定依赖
mvn help:effective-pom                 # 有效 POM
mvn clean                              # 清理构建

# Gradle
gradle dependencies                    # 查看依赖
gradle dependencies --configuration runtimeClasspath  # 运行时依赖
gradle clean                           # 清理构建

# 搜索
rg "关键字" --type java --type xml     # 搜索代码
find . -name "*.xml" -exec grep -l "关键字" {} \;  # 查找文件

# 运行时调试
java -jar app.jar --debug              # 开启自动配置报告
java -jar app.jar --logging.level.root=DEBUG  # DEBUG 日志

posted @ 2026-03-25 23:50  景之1231  阅读(13)  评论(0)    收藏  举报