什么是循环依赖
A类的创建依赖与B类,B类的创建依赖于A类
有4种简单的情况
依赖情况 | 依赖注入方式 | 循环依赖是否被解决 |
---|---|---|
AB相互依赖(循环依赖) | 均采用setter方法注入 | 是 |
AB相互依赖(循环依赖) | 均采用构造器注入 | 否 |
AB相互依赖(循环依赖) | A中注入B的方式为setter方法,B中注入A的方式为构造器 | 是 |
AB相互依赖(循环依赖) | B中注入A的方式为setter方法,A中注入B的方式为构造器 | 否 |
以上情况后续会讲
Spring怎么去解决循环依赖
三级缓存
在介绍循环依赖之前,先介绍几个集合
Map | 作用 |
---|---|
三级缓存singletonFactories | 保存要创建Bean的Factory |
二级缓存earlySingletonObjects | 保存根据Bean的Factory创建出来的Bean代理对象 |
一级缓存singletonObjects | 保存创建好的Bean |
registeredSingletons | 表明集合中的Bean被注册 |
singletonsCurrentlyInCreation | 表明集合种的Bean正在被创建 |
Bean的循环依赖与Bean的创建密不可分,请先了解Bean的生命周期
- doGetBean
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
Object sharedInstance = this.getSingleton(beanName);
-----
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
在doGetBean中,这两个点是和循环依赖紧密相关的
先看getSingletonString beanName, boolean allowEarlyReference)源码,在上文说的A和B的循环依赖过程中该方法会执行3次;一次是创建A时,因为A也是第一次创建,所以返回为空,不会执行具体流程,当A的生命周期走到属性注入,即org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean,并且要注入B,这时会去创建B,然后创建B的时候再执行一遍,因为B也是第一次创建,所以返回为空,当B进行属性注入,并且要注入A时,又会调用一次该方法,同时因为A已经在被创建,并且A的工厂已经加入到singletonFactories中所以在该方法执行结束后会返回A的代理对象,用于创建B的Bean,那么A的工厂又是在什么时候添加到singletonFactories中的呢?请接下来看org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//判断单例池中是否存在已经创建好的Bean
Object singletonObject = this.singletonObjects.get(beanName);
//不存在并且该Bean正在被创建
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
//判断二级缓存中是否存在该Bean的代理
singletonObject = this.earlySingletonObjects.get(beanName);
//不存在并且该Bean允许循环依赖
if (singletonObject == null && allowEarlyReference) {
//对一级缓存加锁,防止重复创建Bean
synchronized(this.singletonObjects) {
//再次判断一级缓存中是否存在创建好的Bean
singletonObject = this.singletonObjects.get(beanName);
//不存在
if (singletonObject == null) {
//再次判断二级缓存中是否存在该Bena的代理对象
singletonObject = this.earlySingletonObjects.get(beanName);
//不存在
if (singletonObject == null) {
//再次判断三级缓存中是否存在该Bena的工厂
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
//存在
if (singletonFactory != null) {
//获取到对应的工厂
singletonObject = singletonFactory.getObject();
//使用该Bean的工厂创建出Bean的代理对象放入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
//移除三级缓存中的Bean工厂
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
该方法是在进行属性注入之前执行的
源码
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
//对一级缓存加锁
synchronized(this.singletonObjects) {
//如果一级缓存是不存在该Bean
if (!this.singletonObjects.containsKey(beanName)) {
//将该Bean的factoru加入三级缓存
this.singletonFactories.put(beanName, singletonFactory);
//将该Bean对应的代理移出二级缓存
this.earlySingletonObjects.remove(beanName);
//将该Bean进行注册
this.registeredSingletons.add(beanName);
}
}
}
然后看一下另一个getSingleton方法org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
该方法是在doGetBean时触发的,用于获取单例Bean,在这可以想一想,获取不到该Bean会怎么处理?获取不到当然就创建了,在该方法中会对creatBean进行调用
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, () -> {
try {
//调用CreatBean创建Bean
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
在creatBean中会先对要创建的Bean进行校验,校验完成后调用doCreatBean开始创建Bean
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
......
try {
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
......
}
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
也就是在该方法中,完成了Bean的实例化,属性注入,初始化
循环依赖也就是在属性填充的时候被触发的,如果触发了循环依赖,则会创建需要循环依赖的对象,如果该对象正在被创建并且加入到了三级缓存(上文提到过),则会通过三级缓存创建出一个代理对象,用来创建需要的创建的对象。当对象被创建完成后会加入到一级缓存中,然后交给需要被创建的对象去创建。(当AB存在循环依赖,A先被创建,在属性注入时去创建了B,又开始创建B,对B进行属性注入时,发现依赖A,又去获取A,发现A正在被创建,并且已经存在于三级缓存中,就创建A的代理对象交给B,让B进行创建,创建完成后放入一级缓存,然后返回去创建A,A去一级缓存拿到B,再将A创建完成…就是这样子)。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
......
if (instanceWrapper == null) {
//实例化
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
......
try {
//属性填充
this.populateBean(beanName, mbd, instanceWrapper);
//初始化
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
}
......
}
上面说了创建好的Bena要加入到一级缓存,那么是在哪里加入的呢?还记不记得我们是在哪里调用的creatBean了?是在
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)这里对吧!在这里创建肯定也是在这个方法里执行添加的啊!看这个方法的源码会发现,在该方法中存在这样一个方法org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton
来看下源码,可以看出来加入到了一级缓存中了,也就是我们常说的单例池!
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);
}
}
循环依赖就这样被解决的
不知道大家感觉到没,Spring的循环依赖是在进行属性注入的时候才会触发进行解决,也就是在进行setter时,并不存在使用构造方法时的循环依赖处理,在自己尝试时发现,依据构造方法注入的循环依赖是无法被解决的,也就是最开始的循环依赖解决的表,我把它复制过来了,但是第三种却是可以成功的,第4中却不行,下面说下我自己的理解,如果不对请及时告知!
依赖情况 | 依赖注入方式 | 循环依赖是否被解决 |
---|---|---|
AB相互依赖(循环依赖) | 均采用setter方法注入 | 是 |
AB相互依赖(循环依赖) | 均采用构造器注入 | 否 |
AB相互依赖(循环依赖) | A中注入B的方式为setter方法,B中注入A的方式为构造器 | 是 |
AB相互依赖(循环依赖) | B中注入A的方式为setter方法,A中注入B的方式为构造器 | 否 |
在说之前,我们要知道一个点,就是Spring在创建Bean的时候是有顺序的,会按照首字母或者一定的方式进行排序,大家可以查一下,在这里就可以得出,A会在B之前被创建。
第三种情况下,A的生命周期会走到属性注入,发现需要注入B,就会去创建B,在创建B的过程中发现需要注入A,又因为A已经进行了实例化,所以B可以使用A实例化后的对象进行创建,也就解决了循环依赖的问题。
第四种情况下:在A进行实例化时就需要用到B,所以会直接去创建B,这时并没有将A实例化完成,没有加入到缓存,以致于在创建B的时候又要回来重新开始创建A,创建A时又要创建B,就这样来回循环。。。就解决不掉了。。。
然后对面试常问的几个问题进行下说明
- 用二级缓存能解决循环依赖吗??
答案是可以的,下面看下分别去掉三级缓存和二级缓存对循环依赖有什么影响
先去掉三级缓存,在三级缓存中存的是用来创建Bena的工厂,去过去掉了,每次将直接创建代理对象放入二级缓存中,,这样也能处理循环依赖,但是会有什么影响?对比去掉前和去掉后会发现,去掉后会在实例化阶段执行后置处理器,进行对象的代理,而这有背与Spring的设计,会对之后的扩展造成影响,而有了三级缓存之后,就可以在真正需要的时候进行代理,相当于延迟了实例化。
再看看去掉二级缓存,二及缓存是用来存放代理对象的,而去掉二级缓存后,代理对象会放到哪里去?大概率会是一级缓存中,这样再一级缓存中可能就会存放不同时期的Bean形式,代理和完全的Bean,假如A和B都依赖于C,在创建A的时候,会依赖于C的代理创建出来A,然后一级缓存中的C执行完生命周期将会变成完整的Bean,这是B再创建,B就会根据一级缓存中的C的Bean来创建Bean,这样就会导致,A和B依赖了不同形式的C。 - 三级缓存会提高效率吗?
可以回答影响不大
如果使用三级缓存并且存在缓存依赖的情况下,对于效率的影响主要是在于对AOP的处理上
如果使用三级缓存,对于有无AOP的情况,只是进行AOP代理的时机不同,存在循环依赖时,会在处理二级缓存时进行代理,不存在的化则会在初始化时进行代理;
如果使用二级缓存,同样是需要代理,同样也是代理的时机不同而已
对于以上的情况来说,对于效率的影响可以说是没有。