Spring 通过三级缓存机制来解决循环依赖问题。以下是 Spring 解决循环依赖的详细过程:
1. 三级缓存的作用
Spring 使用三级缓存来管理 Bean 的创建和依赖注入:
- Singleton Objects Cache(一级缓存)
存放完全初始化好的单例 Bean。
Key: Bean 名称,Value: 完全初始化的 Bean 实例。 - Early Singleton Objects Cache(二级缓存)
存放提前暴露的 Bean(半成品 Bean,尚未完成属性注入和初始化)。
Key: Bean 名称,Value: 提前暴露的 Bean 实例。 - Singleton Factories Cache(三级缓存)
存放 Bean 的工厂对象(ObjectFactory
),用于创建 Bean 的早期引用。
Key: Bean 名称,Value:ObjectFactory
。
2. 解决循环依赖的流程
以下是一个典型的循环依赖场景:
BeanA
依赖于BeanB
,BeanB
又依赖于BeanA
。
解决步骤:
- 创建
BeanA
- Spring 开始创建
BeanA
,首先调用BeanA
的构造函数,生成一个半成品对象(此时BeanA
还未完成属性注入和初始化)。 - 将
BeanA
的工厂对象(ObjectFactory
)放入三级缓存中。
- Spring 开始创建
- 注入
BeanA
的依赖- Spring 发现
BeanA
依赖于BeanB
,于是开始创建BeanB
。
- Spring 发现
- 创建
BeanB
- Spring 调用
BeanB
的构造函数,生成一个半成品对象(此时BeanB
还未完成属性注入和初始化)。 - 将
BeanB
的工厂对象(ObjectFactory
)放入三级缓存中。
- Spring 调用
- 注入
BeanB
的依赖- Spring 发现
BeanB
依赖于BeanA
,于是尝试从缓存中获取BeanA
。 - 从三级缓存中获取
BeanA
的工厂对象,调用getObject()
方法生成BeanA
的早期引用(半成品)。 - 将
BeanA
的早期引用放入二级缓存,并从三级缓存中移除BeanA
的工厂对象。
- Spring 发现
- 完成
BeanB
的初始化- Spring 将
BeanA
的早期引用注入到BeanB
中,完成BeanB
的属性注入和初始化。 - 将完全初始化的
BeanB
放入一级缓存。
- Spring 将
- 完成
BeanA
的初始化- Spring 将完全初始化的
BeanB
注入到BeanA
中,完成BeanA
的属性注入和初始化。 - 将完全初始化的
BeanA
放入一级缓存。
- Spring 将完全初始化的
3. 代码示例
以下是一个典型的循环依赖示例:
@Component
public class BeanA {
private BeanB beanB;
@Autowired
public void setBeanB(BeanB beanB) {
this.beanB = beanB;
}
}
@Component
public class BeanB {
private BeanA beanA;
@Autowired
public void setBeanA(BeanA beanA) {
this.beanA = beanA;
}
}
在这个例子中:
BeanA
依赖于BeanB
。BeanB
依赖于BeanA
。
Spring 通过三级缓存机制解决了这种循环依赖问题。
4. 注意事项
- 构造函数注入无法解决循环依赖
如果使用构造函数注入,Spring 无法提前暴露 Bean 的早期引用,因此会抛出BeanCurrentlyInCreationException
异常。@Component public class BeanA { private final BeanB beanB; @Autowired public BeanA(BeanB beanB) { this.beanB = beanB; } } @Component public class BeanB { private final BeanA beanA; @Autowired public BeanB(BeanA beanA) { this.beanA = beanA; } }
- Setter 注入或字段注入可以解决循环依赖
Spring 可以在 Bean 实例化后再注入依赖,因此 Setter 注入或字段注入是解决循环依赖的推荐方式。 - 原型(Prototype)作用域的 Bean 不支持循环依赖
Spring 只支持单例(Singleton)作用域的 Bean 解决循环依赖,原型作用域的 Bean 会直接抛出异常。
5. 总结
Spring 通过三级缓存机制解决了单例 Bean 的循环依赖问题:
- 一级缓存存放完全初始化的 Bean。
- 二级缓存存放提前暴露的 Bean(半成品)。
- 三级缓存存放 Bean 的工厂对象,用于生成早期引用。
理解三级缓存机制和循环依赖的解决流程,对于深入掌握 Spring 框架非常重要。
THE END
暂无评论内容