项目技术栈概览
| 技术组件 | GroupId / ArtifactId | 版本 |
|---|---|---|
| JDK | - | 1.8 |
| Spring Boot | org.springframework.boot | 2.6.13 |
| MyBatis-Plus | com.baomidou / mybatis-plus-boot-starter | 3.4.1 |
| 分页插件 | com.github.pagehelper / pagehelper-spring-boot-starter | 1.3.0 |
| MySQL Driver | mysql / mysql-connector-java | 8.0.15 |
| Lombok | org.projectlombok / lombok | (由Spring Boot管理) |
| Hutool | cn.hutool / hutool-all | 5.8.16 |
问题现象
启动 Spring Boot 应用时出现以下错误:
The dependencies of some of the beans in the application context form a cycle:
┌──->──┐
|com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration
└──<-──┘
Action:
Relying upon circular references is discouraged and they are prohibited by default.
Update your application to remove the dependency cycle between beans.
As a last resort, it may be possible to break the cycle automatically by setting
spring.main.allow-circular-references to true.
错误信息解读:
- 不鼓励依赖循环引用,并且默认情况下禁止循环引用
- 需要更新应用程序以删除bean之间的依赖循环
- 作为最后的手段,可以通过设置
spring.main.allow-circular-references=true来自动打破循环
问题根因分析
这是一个典型的 Spring Boot 循环依赖错误,具体原因如下:
-
循环依赖本质 :
PageHelperAutoConfiguration自动配置类与其他Bean之间形成了循环引用- Bean A 依赖于 Bean B
- 同时 Bean B 又依赖于 Bean A
- Spring 无法确定创建顺序,导致启动失败
-
版本变更影响:PageHelper 分页插件的自动配置可能与数据源或 MyBatis 配置产生循环依赖。由于 Spring Boot 2.6+ 版本默认禁止了循环引用,导致此问题暴露出来
-
技术背景:Spring Boot 2.6.0 版本开始默认禁止循环引用,这是该版本的一个重要变更
有关 Spring Boot 2.6.0 版本新特性的详细说明,请参考:Spring Boot 2.6.0新特性解析
解决方案汇总
方案一:排除自动配置 + 手动配置(推荐)
这是最彻底且推荐的解决方案,能够从根本上解决问题。
实现步骤:
-
排除自动配置类
java@SpringBootApplication(exclude = {PageHelperAutoConfiguration.class}) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } } -
创建手动配置类
java@Configuration public class PageHelperConfig { @Bean @ConfigurationProperties(prefix = "pagehelper") public Properties pageHelperProperties() { return new Properties(); } @Bean public PageInterceptor pageInterceptor(Properties pageHelperProperties) { PageInterceptor pageInterceptor = new PageInterceptor(); pageInterceptor.setProperties(pageHelperProperties); return pageInterceptor; } /** * 关键步骤:确保拦截器被正确添加到SqlSessionFactory */ @Bean public SqlSessionFactoryBean sqlSessionFactoryBean( SqlSessionFactoryBean factoryBean, PageInterceptor pageInterceptor) { factoryBean.setPlugins(new Interceptor[]{pageInterceptor}); return factoryBean; } } -
配置文件参数(application.yml)
yamlpagehelper: helper-dialect: mysql reasonable: true support-methods-arguments: true params: count=countSql
优势:
- 彻底解决循环依赖问题
- 保持配置的灵活性
- 符合 Spring Boot 2.6+ 的设计理念
方案二:临时解决方案(快速修复)
在配置文件中允许循环引用:
yaml
# application.yml
spring:
main:
allow-circular-references: true
properties
# application.properties
spring.main.allow-circular-references=true
适用场景:
- 紧急修复,快速恢复服务
- 开发环境临时使用
注意事项:
- 这只是临时绕过问题,并未真正解决循环依赖
- 不建议在生产环境中长期使用
- 应计划在后续版本中改用方案一
方案三:版本降级(不推荐)
将 Spring Boot 版本降级到 2.6 之前:
xml
<!-- 降级到 2.5.x 版本 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.14</version>
</parent>
为什么不推荐降版?
- 掩盖根本问题:循环依赖如同"定时炸弹",降版本只是关闭警报,并未拆除隐患
- 丧失新特性优势:新版本带来性能优化、功能增强和安全更新,降版意味着放弃这些改进
- 版本管理规范:在项目开发中,一旦确定技术栈版本不应随意更改,否则会引发一系列兼容性问题,增加维护成本
- 技术债务积累:暂时回避问题会导致技术债务积累,未来升级时将面临更大挑战
- 社区支持减弱:旧版本逐渐失去官方支持和安全更新
方案四:依赖优化与代码重构
-
检查依赖兼容性
确保 PageHelper 版本与 Spring Boot 版本兼容
版本兼容性参考:SpringBoot与PageHelper版本兼容指南
-
优化Bean初始化顺序
使用
@DependsOn注解明确Bean依赖关系 -
使用延迟加载
在可能产生循环依赖的Bean上添加
@Lazy注解java@Configuration public class MyConfig { @Bean @Lazy public SomeService someService() { return new SomeService(); } } -
架构层面解耦
检查是否存在设计层面的循环依赖,考虑通过接口抽象或事件驱动等方式解耦
综合建议与实践指导
- 首选方案(强烈推荐):方案一
- 使用排除自动配置 + 手动配置的方式
- 保持配置文件参数化的优势
- 从根本上解决问题,符合最佳实践
- 临时应急:方案二
- 紧急情况下可临时启用循环引用
- 但必须制定计划迁移到方案一
- 绝对避免:方案三
- 版本降级会带来更多潜在问题
- 不符合技术演进的方向
- 架构优化:方案四
- 作为长期架构优化的方向
- 结合方案一实施效果更佳
常见问题排查
如果手动配置后分页不生效,请检查:
- 拦截器是否正确注入 :确保
PageInterceptor被添加到SqlSessionFactory - 方言配置是否正确 :检查
helper-dialect参数是否与数据库类型匹配 - 配置参数完整性:确认必要参数都已正确配置
- 版本兼容性:验证 PageHelper 与 Spring Boot 版本兼容性
总结
Spring Boot 2.6+ 版本禁止循环引用是合理的架构改进,它帮助开发者发现潜在的设计问题。面对 PageHelper 引发的循环依赖错误,我们应当:
- 优先选择手动配置的方式彻底解决问题
- 避免简单粗暴的版本降级或长期允许循环引用
- 建立版本管理规范,确保技术栈的稳定性和可维护性
- 定期进行架构审查,预防类似的循环依赖问题
通过正确的解决方案,不仅能够解决当前问题,还能提升应用程序的整体质量和可维护性。