温馨提示:在学习循环依赖之前,先学习SpringBean的生命周期
一、循环依赖是什么?
Spring有个很重要的特性,在容器创建某个对象的时候,发现这个对象中的属性需要注入另一个对象,此刻会先去创建另一个对象。
根据上述特性,对象之间如果存在“你中有我,我中有你",对象间互相依赖的情况,就构成了循环依赖。
如下代码所示(你心中有我,我心中有你):
HeartA:
@Data
@Component
public class HeartA {
@Autowired
HeartB heartB;
}
HeartB:
@Data
@Component
public class HeartB {
@Autowired
HeartA heartA;
}
二、解决方法
1. 提前暴露法。
在初始化 HeartA
的属性之前,也就是注入 HeartB
对象之前,先把 HeartA
实例化后的对象放到一个缓存中,提前暴露实例化后的半成品的HeartA
,这样在创建 HeartB
注入 HeartA
对象时,先从这个缓存中获取 HeartA
实例化后的对象,给HeartB
注入一个半成品的HeartA
,这样就解决了循环创建的问题。如下图所示:
2. 懒加载@Lazy
将@Lazy 放在@Autowired注解的属性上,在注入的时候并不会直接给属性赋上真正的值,只会注入一个代理对象,当真正使用到这个属性的时候,才回去容器中找到一个符合的对象。
三、三级缓存
getSingleton:
- 一级(singletonObjects):存放成品对象
- 二级(earlySingletonObjects):存放半成品对象
- 三级(singletonFactories):用于获取提前暴露的半成品对象
singletonFactory.getObject():
获取提前暴露的bean,对于特殊的bean,在getObject中有特殊的处理。
getObject()核心代码是getEarlyBeanReference,在这个方法中调用所有的SmartInstantiationAwareBeanPostProcessor的实现类,对bean做特殊处理,如果是个普通bean,每次getObject()返回的都是同一个bean,如果是需要特殊处理的bean,比如需要Aop织入,则每次都会特殊处理一次,每次获取的都是不同的bean。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
第二节的流程图是通过二层缓存(singletonObjects、singletonFactories)解决循环依赖的,但为什么要用到三层缓存(earlySingletonObjects)呢?
原因:在创建的过程中,实例化后的bean,并不会传入到每个方法中,都是用getSingleton(beanName)的方式获取当前状态的bean。创建的对象只是一个普通对象的话,多次getSingleton()返回的是同一个bean。如果创建的是需要特殊处理的bean,多次getSingleton()返回的都是不同的bean,设置earlySingletonObjects缓存,在第一次特殊处理bean之后,就将特殊处理的bean存入earlySingletonObjects缓存,这样每次getSingleton()获取bean的时候,会先尝试从earlySingletonObjects缓存中获取,不再创建特殊bean了。
举个例子:第一次调用getSingleton(“heartA”),获取到了代理对象A1,随后对这个A1对象做了一些操作,之后又调用了一次getSingleton(“heartA”),获取到了代理对象A2,这个时候就有问题了,创建heartA的过程中,同时出现了两个不同的对象。
四、源码分析
1. doCreateBean中提取暴露bean
实例化后,判断有循环依赖,将实例化后的bean存入singletonFactories中,提取暴露bean。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
....代码省略
// 实例化bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
....代码省略
// 可以修改BeanDefinition的扩展点
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
// 此处是如果发生了循环依赖的情况,提前暴露没创建完的bean,可以称为半成品的bean或实例化bean
// getEarlyBeanReference方法中执行SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法
// getEarlyBeanReference中可以对bean做一些操作,主要应用是Aop织入
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 提前暴露bean的获取,存入singletonFactories中,调用singletonFactory.getObject()获取bean
// singletonFactory.getObject() = () -> getEarlyBeanReference(beanName, mbd, bean)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 属性赋值
populateBean(beanName, mbd, instanceWrapper);
// 初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
....代码省略
// 这里判断在初始化的过程中,bean是否还是原来的bean,如果不是就会在下面的分支中报错
....代码省略
// 注册到 disposableBeans 里面.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
2. getSingleton获取提前暴露的半成品对象:
3. getObject
中又调用的getEarlyBeanReference
获取所有的SmartInstantiationAwareBeanPostProcessor对bean特殊处理,没有特殊处理,直接返回传入的bean(exposedObject)。
4. SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference():
下面代码是实现类AbstractAutoProxyCreator的getEarlyBeanReference()
5. wrapIfNecessary
在这个方法中,判断这个bean需不需要创建代理对象,需要则创建代理对象,反之返回传进来的原bean。
6. 把第一次获取到的半成品对象,放入earlySingletonObjects缓存,避免多次重复创建代理对象。
五、总结
- 循环依赖的构成,是因为多个对象间的互相依赖造成的。
- 解决办法:提前暴露实例化后的bean或者懒加载。
- 用三级缓存的原因是,防止多次getSingleton导致多次创建代理对象。