现在有两个对象Water和Ice,这两个对象相互引用,代码如下:
代码Ice类
package com.xiaolinzi.cyclicdepen;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author :xiaolinzi
* @date :2020-4-14 20:46
* @email : xiaolinzi95_27@163.com
*/
@Component
public class Ice {
@Autowired
private Water water;
}
Water类
package com.xiaolinzi.cyclicdepen;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author :xiaolinzi
* @date :2020-4-14 20:46
* @email : xiaolinzi95_27@163.com
*/
@Component
public class Water {
@Autowired
private Ice ice;
}
Water和Ice两个类都是单例,并且在属性值上相互依赖,那么spring是怎么解决这个问题的呢?下面从一张图和一段代码中来看一下spring如何解决循环依赖问题的:
流程图
流程:
1.如果我们是获取Water这个对象,它会首先调用AbstractApplicationContext类的getBean(Water.class)方法;
2.第二步调用AbstractBeanFactory的doGetBean(water ...)方法来获取实例化water;
3. 在获取bean的时候,spring首先会从三级缓存中获取water,也就是调用getSingleton(water)方法,这个方法很重要,流程结束后我会附上这个方法源码及过程;
4.因为是第一次获取water,所以是获取不到的,这时候会调用DefaultSingletonBeanRegistry的getSingleton(beanName, singletonFactory)去获取water;
5.首先spring会冲一级缓存singletonObjects获取water,因为第一次来获取,缓存中是没有这个对象的。然后spring首先会把water放入singletonsCurrentlyInCreation中,只有这样,water在后面才会被标记要被创建;
6.然后spring执行singletonFactory.getObject()代码,回调了.AbstractAutowireCapableBeanFactory的createBean(water, mbd ,args)方法进行创建对象
7.然后spring调用了doCreateBean(water,mbd,args)方法,这个方法中做了下面几件事情 1. 执行createBeanInstance(water,...)方法通过构造方法或者工厂方法把water实例化出来;2. 调用DefaultSingletonBeanRegistry的addSingletonFactory(water,singletonFactory)方法,把早期对象放入三级缓存 3. 执行populateBean(water, mbd, instanceWrapper)方法
8. 调用DefaultSingletonBeanRegistry的addSingletonFactory(water,singletonFactory)方法,把早期对象放入三级缓存
9.执行populateBean(water, mbd, instanceWrapper)方法,对属性进行赋值,但是这时候缺发现water对象依赖ice对象,这时候怎么办呢,会调用图右边ice实例化的过程1至过程9 ,实例化Ice对象执行到9时,发现Ice对象依赖water对象,这时候就回去缓存中获取water对象,而恰巧获取到了water 的早期对象,这时候ice对象被实例化,同时water对象也被实例化。
10.这时候water已经被实例化了,执行一些后续步骤,比如执行DefaultSingletonBeanRegistry的addSingleton(water,singletonObject)方法,把对象放入缓存 等等操作
11.返回water。
三级缓存是什么?
上面第三部中,从缓存中获取bean,是怎么获取的,缓存是什么?我们通过spring源码加上注释来解释一下:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
//多例是因为没放入缓存所以循环依赖也不允许
@Nullable //这种没办法解决构造器依赖注入,因为构造器方法createBeanInstance在往缓存中暴露早起对象之前就调用了
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //下面是多级缓存解决循环依赖的问题
Object singletonObject = this.singletonObjects.get(beanName);//singletonObjects 是单例缓存池,一级缓存,完整对象
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { //对象为空并且正在创建对象
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);//二级缓存
if (singletonObject == null && allowEarlyReference) { //allowEarlyReference 允许默认引用,spring默认true
ObjectFactory singletonFactory = this.singletonFactories.get(beanName); //三级缓存
if (singletonFactory != null) { //从三级缓存拿出来放到二级缓存
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
三级缓存是什么?
singletonObjects:单例缓存池为一级缓存;
earlySingletonObjects:早期对象为二级缓存;
singletonFactories:为三级缓存。
获取缓存的过程:
首先是从singletonObjects一级缓存中获取bean,如果获取到就返回,如果获取不到并且该对象正在创建(isSingletonCurrentlyInCreation),从二级缓存中拿出早起对象来,如果早起对象为空并且允许引用(allowEarlyReference 这是个标识,spring默认是true),则从三级缓存中获取到singletonFactory,然后执行singletonFactory.getObject() 也就是回调上述流程图中的步骤不,回调createBean接口,进行实例化bean。
在构造函数中相互依赖是否可以?
如果代码改成下面的方式是否可以?
water类:
package com.xiaolinzi.cyclicdepen;
import org.springframework.stereotype.Component;
/**
* @author :xiaolinzi
* @date :2020-4-14 20:46
* @email : xiaolinzi95_27@163.com
*/
@Component
public class Water {
private Ice ice;
public Water(Ice ice) {
this.ice = ice;
}
}
Ice类
package com.xiaolinzi.cyclicdepen;
import org.springframework.stereotype.Component;
/**
* @author :xiaolinzi
* @date :2020-4-14 20:46
* @email : xiaolinzi95_27@163.com
*/
@Component
public class Ice {
private Water water;
public Ice(Water water) {
this.water = water;
}
}
如果你这么依赖,运行时会报以下错误
Error creating bean with name 'ice' defined in file [E:\java\workspace\spring-study\target\classes\com\xiaolinzi\cyclicdepen\Ice.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'water' defined in file [E:\java\workspace\spring-study\target\classes\com\xiaolinzi\cyclicdepen\Water.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'ice': Requested bean is currently in creation: Is there an unresolvable circular reference?
这是什么原因呢?为什么会这样?
因为spring在堆属性赋值之前,会先执行上述流程第7步中的AbstractAutowireCapableBeanFactory的createBeanInstance方法,这一步主要是把通过构造方法或者工厂方法的对象实例化出来,然后再执行第九步的属性赋值方法,因此这个会先执行构造方法,再对属性赋值,因为通过构造方法的相互引用会有误。
如果设置对象为多例,会不会将对象放入缓存?
我们所说的spring的三级缓存都是针对spring单实例的,如果设置成多例的,不会讲对象放入缓存中。
作者:小林小胖子