目录:
1、简介
2、什么是循环依赖?
3、Spring 是如何解决循环依赖的?
4、Spring 解决循环依赖的源码分析
5、为什么需要三级缓存呢?
6、循环依赖的其他情况
7、总结
简介
下文将介绍Spring是如何解决循环依赖的,在下文中将要介绍以下什么是循环依赖。以及从源码层面上分析Spring是如何解决的,最后会介绍哪几种情况下的循环依赖 是Spring 解决不了的。
Spring 如何解决循环依赖也是高频面试题,面试官的可以考察你对循环依赖的理解,以及你对源码的熟悉程度,以及哪些情况下的循环依赖是Spring解决不了的。
什么是循环依赖?
所谓的循环依赖就是A依赖B,B依赖A,或者是A依赖B,B依赖C,C依赖A,最终形成一个环状的依赖。
代码示例如下:
从上面的代码看,如果Spring不处理循环依赖,会怎么样?
IOC容器在读取上面配置时,首先会初始化对象A,然后发现对象A依赖对象B,就去初始化对象那个B,初始化对象B的时候,又发现依赖A,需要初始化A,如果容器不去处理,那么将会无限循环下去 ,直到内存溢出。
那么Spring是如何解决循环依赖的呢?
Spring 是如何解决循环依赖的?
在初始化A时,即在调用构造方法时,会 产生一个早期引用,存在缓存中,然后A发现需要依赖B对象,容器再去初始化B对象,B对象初始化后(调用构造方法,也会产生早期引用,存在缓存中),A这时就得到了B的对象(已实例化,但是还没有注入属性),然后B对象发现需要依赖A对象,这时A对象已经有一个早期引用了,所以B直接依赖对象A,
什么是早期引用呢?
即 对象实例化后(调用构造方法),还没有进行属性注入,如下图:
在 注入属性之前的对象A就是早期对象
Spring 依赖是通过三级缓存来实现的,分类如下:
1、三级缓存(singletonFactories)
用于存放 beanName和 初始化好的bean对象(属性已经初始化好的)
private final Map singletonObjects = new ConcurrentHashMap(256);
用于存放bean工厂。bean 工厂所产生的bean是还未完成初始化的bean.bean工厂所生成的对象最终会被缓存到earlySingletonObjects(二级缓存)中,包裹对象ObjectFactory.getObject()早期对象。
放入时机:调用类的构造方法初始化之后。
ObjectFactory.getObject()->触发getEarlyBeanReference()之后,才把我们的早期对象放入二级缓存中。
2、二级缓存(earlySingletonObjects)
存放 bean 工厂对象,用于解决循环依赖
private final Map> singletonFactories = new HashMap>(16);
在三级缓存放入二级缓存中调用:
早期对象:就是bean刚刚调用了构造方法,还来不及给bean对象的 属性进行赋值的对象
3、一级缓存(singletonObjects)
用于存放beanName 和一个原始bean 早期bean(属性未初始化)
private final Map earlySingletonObjects = new HashMap(16);
用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用。
Spring 解决循环依赖的源码分析
我们来了解一下Spring IOC容器获取bean实例的流程:
我们来分析一下上图的流程:
这个流程从getBean开始,所有的逻辑都在doGetBean中,doGetBean首先调用getSington方法获取bean实例,获取的实例分为三种情况:
(1) 完全实例化好的bean(此时bean的属性已经注入)
(2) 早期对象(bean已经调用构造方法进行初始化,但是属性还没有注入)
(3) 各级缓存中都没有,则返回null
如果不为null,则调用 getObjectForBeanInstance 方法来返回bean。getObjectForBeanInstance 方法作用是如果Bean是FactoryBean,则调用其 getObject()方法。
如果bean为null,则 调用getSingleton方法,
sharedInstance = getSingleton(beanName, new ObjectFactory() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
}
});
最终会调用createBean方法,进行bean的实例化。
首先看下调用链;
AbstractBeanFactory# doGetBean
1、从缓存中获取bean对象
Object sharedInstance = getSingleton(beanName);
方法如下:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//第一步
Object singletonObject = this.singletonObjects.get(beanName);
i
f (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 != NULL_OBJECT ? singletonObject : null);
}
执行步骤如下:
第一步: 我们尝试从一级缓存中去获取对象,一般直接从map中获取是可以直接使用的,IOC容器初始化加载单实例bean的 时候第一次进来,一般为空
第二步: 若在一级缓存中没有获取到对象,并且此bean正在初始化,singletonsCurrentlyInCreation这个list包含该beanName,主要用于判断bean是否在创建中。
第三步:尝试去二级缓存中获取对象(二级缓存中的 对象也是一个早期对象(已经调用构造方法,但是还没有对属性赋值)。
第四步:二级缓存中也没有,那就直接从三级缓存中获取ObjectFactory对象,这个对象就是用来解决循环依赖 的关键所在,
第五步:如果有三级缓存,则调用ObjectFactory包装对象中,通过调用它的getObject()来获取我们的早期对象,然后把早期对象放入二级缓存,从三级缓存中删除掉。
2、如果缓存中没有获取到对象,下面就开始创建对象
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
try {
//调用getObject-->createBean()去创建对象
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
if (newSingleton) {
//把创建好的对象加入到缓存中,并且把早期对象从缓存中删除,此时bean已经完全初始化好
addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 放入到缓存对象中
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
//早期对象逸出
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
上面的代码主要用来创建对象,调用ObjectFactory.getObject()方法创建对象,创建完成后,把早期 对象从缓存中删除。
3、下面看创建对象的方法
AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
// 调用构造方法创建bean对象,并且把bean对象包裹为beanwrapper对象
instanceWrapper = createBeanInstance(beanName, mbd, args);
// earlySingletonExposure 用于表示是否提前暴露原始对象的引用,用于解决循环依赖。
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//把beanName -->ObjectFactory 存放在 singletonFactories缓存中
addSingletonFactory(beanName, new ObjectFactory() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//早期对象进行赋值
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
//调用bean的后置处理器以及bean的初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
return exposedObject;
}
上面方法是真正创建对象的方法,步骤如下
1、调用bean的构造方法进行初始化
2、加入三级缓存中
3、早期对象进行赋值
4、调用Bean的后置处理器以及bean的初始化方法
下面看addSingletonFactory方法源码:
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);
}
}
}
上述方法是把暴露的objectFactory对象放入singletonFactories中。
以上的过程对应的流程图为:
为什么需要三级缓存呢?
有人说,二级缓存不就够了吗?
答案是:二级缓存足够了,但是没有很好的扩展性,我们看下加入三级缓存的源码:
addSingletonFactory(beanName, new ObjectFactory() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
那么 从缓存中获取bean的代码:
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 != NULL_OBJECT ? singletonObject : null);
}
从上面代码可以看出,当获取三级缓存时,需要使用objectFactory.getObject()方法,最终会调用
getEarlyBeanReference(beanName, mbd, bean);方法,
getEarlyBeanReference 方法源码:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
return null;
}
}
}
}
return exposedObject;
}
getEarlyBeanReference()方法的作用是:经过一系列的后置处理来给我们早期对象进行特殊化处理
从三级缓存中获取包装对象的时候 ,他会经过一次后置处理器的处理对我们早期对象的bean进行
特殊处理,但是Spring原生后置处理器没有经过处理,是留给我们的扩展接口。
循环依赖的其他情况
1、构造方法循环依赖能解决吗?
我们先看下示例代码:
报错信息如下:
报错信息如上图:
大概意思是:Spring 解决不了构造方法的循环依赖。
那么为什么Spring不能解决循环依赖呢?
从我们上面分析可知,如果是成员属性有循环依赖,那么Spring是可以解决的,是因为有早期对象的存在才可以解决,早期对象就是实例化后的对象(即调用构造方法后的实例)。
如果构造方法中循环依赖,则无法生成早期对象。所以,Spring无法解决构造方法依赖。
2、多例的循环依赖能解决吗?
因为Spring只缓存单例对象,而不缓存多例对象,所以无法从缓存中存活
总结
到这里,本编文章就基本上写完了。由于本人技术能力有限,若文章有错误不妥之处,欢迎大家指出来。好了,本篇文章到此结束,谢谢大家的阅读。