什么是循环依赖?
循环依赖是指两个或多个Bean之间相互依赖,形成闭环的情况。例如:AService依赖BService,而BService又依赖AService。这种场景下,传统的创建顺序无法满足依赖注入的要求。
Spring的三级缓存机制
Spring通过三级缓存机制优雅地解决了循环依赖问题:
1. 第一级缓存:singletonObjects
-
存储完全初始化完成的单例Bean
-
这里的Bean是经过完整生命周期处理的(包括AOP代理)
2. 第二级缓存:earlySingletonObjects
-
存储提前暴露的Bean引用(早期引用)
-
这些Bean尚未完成完整初始化,但可以用于解决循环依赖
3. 第三级缓存:singletonFactories
-
存储Bean的ObjectFactory(工厂对象)
-
通过工厂可以获取Bean的早期引用,必要时创建代理对象
解决循环依赖的详细流程
场景:AService ←→ BService 相互依赖
java
// AService 依赖 BService
@Service
public class AService {
@Autowired
private BService bService;
}
// BService 依赖 AService
@Service
public class BService {
@Autowired
private AService aService;
}
解决步骤:
-
开始创建AService
-
creatingSet.add('AService')- 标记AService正在创建 -
实例化AService普通对象
-
-
AService需要注入BService
-
从单例池获取BService,不存在则开始创建BService
-
-
创建BService
-
creatingSet.add('BService')- 标记BService正在创建 -
实例化BService普通对象
-
BService需要注入AService
-
-
BService获取AService
-
从单例池获取AService,不存在
-
检查
creatingSet发现AService正在创建(循环依赖 detected!) -
从三级缓存获取AService的ObjectFactory
-
执行工厂方法:
getEarlyBeanReference()→ 必要时创建AService代理对象 -
将结果存入二级缓存
earlySingletonObjects,并从三级缓存移除工厂
-
-
BService继续初始化
-
注入AService的早期引用(可能是代理对象)
-
完成其他属性填充
-
执行AOP(如果需要)
-
将完整BService存入单例池
-
-
AService继续初始化
-
注入已创建的BService
-
完成其他属性填充
-
执行AOP(但发现已提前AOP,直接使用早期引用)
-
将完整AService存入单例池
-
关键源码分析
1. 添加单例工厂
java
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 添加到三级缓存
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
在doCreateBean()方法中,Spring会提前暴露Bean的工厂:
java
// 为了解决循环依赖提前缓存单例创建工厂
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
2. 获取单例Bean的逻辑
java
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1. 从一级缓存查找
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 2. 从二级缓存查找
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// 3. 从三级缓存查找并创建早期引用
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 移动到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
3. 提前AOP处理
java
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
// 如果需要AOP,则创建代理对象
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
return proxy;
}
return bean;
}
为什么需要三级缓存?
-
一级缓存:存储完整的单例Bean,保证单例性质
-
二级缓存:存储早期引用,避免多次执行AOP产生多个代理对象
-
三级缓存:存储工厂对象,延迟决策是否需要进行AOP
如果只有二级缓存,每次获取早期引用都需要执行AOP,可能导致创建多个不同的代理对象,违反单例原则。
1471

被折叠的 条评论
为什么被折叠?



