通过前面的文章大家也许能看出些端倪,循环依赖在整个bean的获取流程中出现了多次,而且也是高频面试题,我们今天就该问题做个简单的介绍
1 Spring循环依赖背景
1.1 循环依赖场景
- 属性注入
- 构造器注入
1.2 Spring如何应对
Spring只能解决单例模式下的 属性注入的循环依赖,至于构造器注入和原型模式的依赖注入,Spring会抛出BeanCurrentlyInCreationException 异常
2 Spring如何解决
2.1 三级缓存
阅读过前几篇文章的胖友们,应该对getSingleton 这个方法不陌生吧,下面附上源码哈
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
- 3个Map。又称之为三级缓存,其含义以及功能如下
| 名称 | 功能 |
|---|---|
| singletonObjects | 存储关系为 beanName-> bean instance,存放单例的bean |
| earlySingletonObjects | 存储关系为 beanName->bean instance,它和 singletonObjects的区别是singletonObjects是存放的完整的bean instance,earlySingletonObjects存放的是早期的bean,不一定是完整的(因为spring允许提前暴露,这也是解决循环依赖的关键哈) |
| singletonFactories | 关系为 beanName-> BeanFactory,存放单例的bean 的 factory ,是解决循环依赖的关键 |
- 2个参数 | 名称 | 功能 | | --- | --- | | isSingletonCurrentlyInCreation |判断当前 singleton bean 是否处于创建中。 Spring 解决 singleton bean 的核心就在于提前曝光 bean | | allowEarlyReference |是否允许从 singletonFactories 缓存中通过 getObject() 拿到对象|
代码含义就是从一级逐步查找,知道第三级缓存,但是如果真的走到第三级别了,会从三级上升至2级,对应代码如下
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
而存放三级缓存的地方,在Spring IOC 源码之 createBean 一文中游提及,代码如下
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
那么存放一级缓存的触发时机呢 Spring IOC 源码之 getBean(二) 一文也有出现过
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//....
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
//.....
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}