什么是循环依赖?
循环依赖指的是两个或多个对象之间相互依赖的情况。简单来说,当对象A依赖于对象B,而对象B又依赖于对象A时,就会形成循环依赖.
Spring循环依赖
我们先来看下列服务,结合Bean创建的生命周期可以知道ReimburseService与ExpenseService循环依赖
// 报销单服务
@Component
public class ReimburseService {
@Autowired
private ExpenseService expenseService;
@Autowired
private InvoiceService invoiceService;
}
// 费用服务
@Component
public class ExpenseService {
@Autowired
private ReimburseService reimburseService;
}
解决
Spring解决循环依赖引入三级缓存
Spring中的三级缓存
- singletonObjects
一级缓存,存放完整生命周期的bean- earlySingletonObjects
二级缓存,存放半成品的bean,没有经历初始化过程的Bean- singletonFactories
三级缓存,用来创建bean对象的工厂
三级缓存的底层实现
让我们直接来到Spring源码
// 直接来到实例化方法
// -> doCreateBean(beanName, mbdToUse, args);
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
BeanWrapper instanceWrapper = null;
// 实例化对象-> 这里获取的是普通对象
instanceWrapper = createBeanInstance(beanName, mbd, args);
Object bean = instanceWrapper.getWrappedInstance();
...
// 如果开启了循环依赖(默认开启)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 放入三级缓存 -> 这里放入的是Bean的构造方法(可能是普通对象或是代理对象)->后面详细讲述这个方法
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
// 属性填充
populateBean(beanName, mbd, instanceWrapper);
// 初始化Bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}catch{
...
}
}
在属性填充时,去获取属性值,最终会调用方法
Object beanInstance = getSingleton(beanName, false);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 首先去一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 一级缓存没有 -> 二级缓存获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 二级缓存没有 -> 加锁
synchronized (this.singletonObjects) {
// 重新一级缓存获取
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 一级缓存没有 -> 二级缓存获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 二级缓存没有 -> 三级缓存获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 这里调用的就是getEarlyBeanReference();之前实例化后放入的
singletonObject = singletonFactory.getObject();
// 将创建的实例对象放入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
根据我们上述的情况,当ReimburseService会依赖注入ExpenseService,就会去创建ExpenseService,
ExpenseService又会去依赖注入ReimburseService,这时就会调用三级缓存中的ReimburseService去实例化,现在我们具体看下getEarlyBeanReference(…);
// 来到getEarlyBeanReference()方法
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
// -> 来到下列方法
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
// 获取缓存键
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 将Bean对象放入早期代理引用缓存中
this.earlyProxyReferences.put(cacheKey, bean);
// 对Bean对象进行包装(如果需要)
return wrapIfNecessary(bean, beanName, cacheKey);
}
上述getEarlyBeanReference()可能返回普通对象,也可能返回代理对象,因为正常生命周期,Bean的AOP是在初始化后执行,然后生成代理对象,如果产生了循环依赖,就必须提前生成代理对象.
结合正常的初始化AOP代码:
/**
* 在初始化之后对Bean对象进行后置处理。
*
*/
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 获取缓存键
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// earlyProxyReferences中对应cacheKey的对象一定是个普通对象X,在getEarlyBeanReference方法中放入的,而且是在wrapIfNecessary()方法之前
// 如果X == bean说明这个Bean没有实现AOP功能,直接返回普通bean就可以了
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 如果X != bean,说明这个Bean是个代理对象,需要进入wrapIfNecessary()再次判断执行后置处理器的逻辑
// 里面维护了一个advisedBeans缓存,所以getEarlyBeanReference方法中执行过的AOP不会在执行
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
// 返回Bean对象
return bean;
}
总结
综上:为什么Spring要使用三级缓存来解决循环依赖呢?
正常情况下,其实我们只要两个缓存就可以了,但是由于AOP是在初始化后执行的(AOP产生的是一个代理对象,不是原先的普通Bean对象),所以如果直接拿实例化的普通对象,AOP就会失效.
这里Spring使用了三级缓存,里面存储的是你创建Bean的方法,也就是说可能得到的是个普通对象,也可能是个代理对象.
总得来说,笔者认为打破Spring循环依赖的是二级缓存,也是三级缓存.
A与B,C都循环依赖了,先依赖注入B,B首次会从三级缓存获取A,然后存到二级缓存,等到依赖注入C的时候,C获取A直接从二级缓存获取.