@Configuration自身循环依赖及解决办法

前言

最近做项目,把项目旧的Springboot框架升级到比较新的一个版本2.7.x,是最后支持JDK8的版本,JDK8寿命真长,不过pagehelper报了自身循环依赖的问题,解决方式也很简单,升级pagehelper的版本即可,不过里面有一个@Configuration的自身循环依赖,比较有意思,就分享一下。Springboot从2.6.0开始就默认禁止使用循环依赖,即默认配置不解决循环依赖:Spring Boot 2.6.0 Configuration Changelog · spring-projects/spring-boot Wiki · GitHub

准备demo

依赖Springboot 2.6.0;pagehelper1.3.0(有深意)

XML 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.6.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.6.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.32</version>
        </dependency>

然后写上配置和基础代码

不考虑各种特殊因素,demo

java 复制代码
@RestController
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @GetMapping("/queryUsers")
    public PageInfo<User> userPageInfo(int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = userMapper.getUsers();
        PageInfo<User> userPageInfo = new PageInfo<>(users);
        return userPageInfo;
    }

mapper写上,启动,没有任何意外

bash 复制代码
***************************
APPLICATION FAILED TO START
***************************

Description:

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注解,那么这个就是创建SpringBean对象,就出现自我循环依赖了。

源码分析

既然知道了循环依赖的Bean class,那么查看源码

如上图,我们可以清晰的看见,这个autoconfiguration的加载顺序,和循环依赖的原因,那么为什么@Bean的方法在@Configuration的初始化时调用会造成循环依赖呢,根源是CGLIB动态代理。

如何验证上面的猜测正确:

使用系统变量开启Spring cglib的生成动态代理字节码写入文件模式

复制代码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/huahua/logs"); 

然后debug启动,可以看到当前的@Configuration注解的类先初始化Bean,而且是代理类,关键在于pageHelperProperties的方法已经被代理了,所以才会在@PostConstruct注解的方法调用时创建pageHelperProperties方法的Bean。

反编译代理类

果然pageHelperProperties方法被代理了,重写了。看看CALLBACK_0是否为空

明显是BeanMethodInterceptor处理(@Bean的处理逻辑)

问题就出在这里,使用@Configuration注解的Bean,被初始化后,在Bean还没有完全创建完成,又调用@Bean的方法,因为Springboot的代理机制,@Bean的方法被SpringCGLIB覆写以便创建@Bean方法的Bean,那么就形成自身循环依赖,因为不是构造函数循环依赖,所以Spring自己也能解决,但是Springboot在2.6.0开始不允许循环依赖存在,所以报错。

解决思路

知道了原理,那么解决就很简单了,升级pageHelper的版本即可,升级1.3.1版本后,还是循环依赖,😋,主要是要看代码,1.3.1的代码这里有修改,但是没有从根本上解决问题,上源码

知道1.4.1版本终于解决了这个问题,因为去掉了@Bean的pageHelperProperties的Bean的创建,实际上也没必要创建这个Bean,因为@EnableConfigurationProperties这个属性会在autoconfig自动(boot逻辑)载入@Configuration,所以在初始化一个空的properties有点多余。

修改后,启动正常,分页OK

调用分页:

总结

实际上循环依赖不仅是Spring Bean的循环依赖,还有接口调用的循环依赖,不过Springboot在2.6.0版本已经默认不允许循环依赖,就是不解决这个问题了,如果spring.main.allow-circular-references配置true,那么还是可以跟以前一样,不过不确定什么时候就移除这个配置。另外自身循环依赖比较特别,主要是Springboot下动态代理的结果,本身方法的引用是不会出现循环依赖的。

相关推荐
云烟成雨TD2 分钟前
Spring AI Alibaba 1.x 系列【44】多智能体 - 混合模式、监督者(SupervisorAgent)、自定义模式
java·人工智能·spring
_日拱一卒11 分钟前
LeetCode:23合并K个升序链表
java·数据结构·算法·leetcode·链表·职场和发展
cany100011 分钟前
C++ -- 泛型编程
java·开发语言·c++
lee_curry12 分钟前
第三章 jvm中的对象和执行引擎
java·jvm·执行引擎
格林威13 分钟前
面阵相机 vs 线阵相机:堡盟与海康相机选型差异全解析 附C++ 实战演示
开发语言·c++·人工智能·数码相机·计算机视觉·视觉检测·工业相机
wang090714 分钟前
Linux性能优化之文件系统基础介绍
java·linux·性能优化
迷藏49422 分钟前
# 发散创新:用Locust实现高并发场景下的精准压力测试与性能调优实战在现代微服务架构中,**接口稳定性与响应速度**已成为衡量
java·python·微服务·架构·压力测试
MaxCode-126 分钟前
Chapter 9:企业实战案例与架构沉淀
人工智能·spring·架构
空中海28 分钟前
Nacos 2: Spring Boot Demo 实战
java·spring boot·后端
土豆.exe32 分钟前
Cast Attack:Java 中 Ghost Bits(幽灵比特)引发的新型安全威胁——Java 生态里被忽视的底层风险引发一系列绕过
java·python·安全