Spring循环依赖的解决方法
作者:程序员码小跳
是什么
Spring循环依赖指两个或多个 Bean 相互依赖,形成闭环。
Spring 通过巧妙的机制(如三级缓存)默认支持 setter 注入的循环依赖,但不支持 constructor 注入的循环依赖(会抛异常)。
两种注入方式
A、构造器注入(如 @Autowired 在 constructor 上):容易造成无法解决的循环依赖,不推荐使用【现象:无法new对象,因为互相是构造函数的入参,编译报错】,会抛 BeanCurrentlyInCreationException。
/**
* 通过构造器的方式注入依赖,构造器的方式注入依赖的bean,下面两个bean循环依赖
* 测试后发现,构造器循环依赖是无法解决的
*/
public class ClientConstructor {
public static void main(String[] args) {
new ServiceA(new ServiceB(new ServiceA(new ServiceB())));
}
}B、Setter 注入:通过 setter 方法或字段注入(如 @Autowired 在字段上),Spring 默认支持
@Component
public class A {
@Autowired
private B b; // setter 注入
}
@Component
public class B {
@Autowired
private A a; // setter 注入
}循环依赖的解决办法【重点】
重要结论:Spring 内部通过3级缓存来解决循环依赖,核心是“提前暴露”半成品 Bean(AOP 代理前),允许其他 Bean 先引用。所谓的三级缓存其实就是 Spring 容器内部用来解决循环依赖问题的三个 Map,这三个 Map 在 DefaultSingletonBeanRegistry 类中。

三级缓存详解:
- singletonObjects(一级缓存):我愿称之为单例池,常说的 Spring 容器就是指它(因为默认的bean生命周期是singleton),我们获取单例 bean 就是在这里面获取的,存放已经经历了完整生命周期的Bean对象【存放完全初始化的 Bean】
- earlySingletonObjects(二级缓存):存放早期暴露出来的Bean对象,Bean的生命周期未结束【存放已经实例化,但属性还未填充完整的半成品 bean】
- singletonFactories(三级缓存):存放可以生成Bean的工厂,用于生产(创建)对象【存放FactoryBean工厂池】
创建过程(以 A 依赖 B,B 依赖 A 为例):
- 实例化 A:Spring 调用 getBean(A),创建 A 实例(无属性),放入三级缓存(singletonFactories,作为 lambda:() -> getEarlyBeanReference(A))。
- 注入 A 的属性:发现依赖 B,调用 getBean(B)。
- 实例化 B:类似,创建 B 实例,放入三级缓存。
- 注入 B 的属性:发现依赖 A,从三级缓存获取 A 的工厂,生成早期 A(半成品),放入二级缓存,并注入到 B。
- 完成 B:B 初始化完毕,移到一级缓存。
- 返回到 A:A 继续注入 B(已完成),完成初始化,移到一级缓存。
- 如果涉及 AOP:三级缓存的工厂会生成代理对象,确保循环中代理一致。

getSingleton(): 从容器里面获得单例的bean,没有的话则会创建 bean
doCreateBean(): 执行创建 bean 的操作(在 Spring 中以 do 开头的方法都是干实事的方法)
populateBean(): 创建完 bean 之后,对 bean 的属性进行填充
addSingleton(): bean 初始化完成之后,添加到单例容器池中,下次执行 getSingleton() 方法时就能获取到
Bean的创建顺序
Bean的创建顺序是由BeanDefinition的注册顺序来决定的, 当然依赖关系也会影响Bean创建顺序,比如A依赖B,那肯定B先创建,A后创建。
那BeanDefinition的注册顺序由什么来决定的?
主要是由注解(配置)的解析顺序来决定,顺序如下:
@Configuration @Component @Import-类 @Bean @Import—ImportBeanDefinitionRegistrar
总结
- 调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查找beanA
- 在getSingleton()方法中,从一级缓存中查找,没有,返回null
- doGetBean()方法中获取到的beanA为null,于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的)
- 在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记该bean正在创建中。然后回调匿名内部类的creatBean()方法
- 进入AbstractAutowireCapableBeanFactory#doCreateBean(),先反射调用构造器创建出beanA的实例,然后判断。是否为单例、是否允许提前暴露引用(对于单例一般为true)、是否正在创建中〈即是否在第四步的集合中)。判断为true则将beanA添加到【三级缓存】中
- 对beanA进行属性填充,此时检测到beanA依赖于beanB,于是开始查找beanB
- 调用doGetBean()方法,和上面beanA的过程一样,到缓存中查找beanB,没有则创建,然后给beanB填充属性
- 此时beanB依赖于beanA,调用getsingleton()获取beanA,依次从一级、二级、三级缓存中找,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此时这个singletonObject指向的就是上面在doCreateBean()方法中实例化的bean
- 这样beanB就获取到了beanA的依赖,于是beanB顺利完成实例化,并将beanA从三级缓存移动到二级缓存中
- 随后beanA继续他的属性填充工作,此时也获取到了beanB,beanA也随之完成了创建,回到getsingleton()方法中继续向下执行,将beanA从二级缓存移动到一级缓存中

到此这篇关于Spring循环依赖详解的文章就介绍到这了,更多相关Spring循环依赖内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
