spring IOC源码 - 单实例bean的循环依赖 源码及过程

Karima ·
更新时间:2024-09-20
· 887 次阅读

现在有两个对象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单实例的,如果设置成多例的,不会讲对象放入缓存中。

 

   

 

 

 

 

 

 

 

 

 

 

 

 

 


作者:小林小胖子



spring 循环 bean ioc

需要 登录 后方可回复, 如果你还没有账号请 注册新账号