Spring Bean 的循环依赖
什么是循环依赖?
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如instanceA依赖于instanceB,instanceB依赖于instanceA。
如下图:
Spring中循环依赖场景:
(1) 构造器的循环依赖。
<bean id="instanceA" class="com.itcast.InstanceA">
<constructor-arg name="instanceB" ref="instanceB"></constructor-arg>
</bean>
<bean id="instanceB" class="com.itcast.InstanceB">
<constructor-arg name="instanceA" ref="instanceA"></constructor-arg>
</bean>
(2) field属性的循环依赖。
<bean id="instanceA" class="com.itcast.InstanceA">
<property name="instanceB" ref="instanceB"></property>
</bean>
<bean id="instanceB" class="com.itcast.InstanceB">
<property name="instanceA" ref="instanceA"></property>
</bean>
Spring Bean 循环依赖数据流程图
IOC容器在启动的过程中会把所有的单例对象实例化后放到单例缓存池中(singletonObjects)。
createBeanInstance:通过反射创建一个早期对象(空壳对象);实例化,其实也就是调用对象的构造方法实例化对象,此时实例化对象没添加任何属性。
addSingletonFactory(String beanName,ObjectFactory<?> singletonFactory):早期对象创建成功后直接放到三级缓存中。
populateBean(String beanName,RootBeanDefinition mbd,@Nullable BeanWrapper bw):给我们的早期对象属性赋值。
从三级缓存中获取早期对象时会触发getEarlyBeanRefernce()方法;getEarlyBeanRefernce()方法拿到的时代理对象引用。
BeanPostProcessor:SpringBean的后置处理器(代理对象才会走后置处理器)。
从三级缓存中拿到早期对象后,把早期对象放置到二级缓存并且ObjectFactory包装对象从三级缓存中删除掉。
initializeBean():进行对象初始化操作(在这里可能生成代理对象)。
wrapIfNecessary()方法:AOP源码知识点,这个方法会生成代理对象(AOP、事务的代理对象都在这个方法中生成)。
addSingleton(String beanName,Object singletonObject):创建bean后调用此方法加入到单例缓存池中,从三级缓存中移除(针对的不是处理循环依赖的)其从二级缓存中移除(循环依赖的时候早期对象存在于二级缓存),用来记录保存已经处理的bean(registeredSingletons)。
doGetBean():真正的获取bean对象的逻辑;
getSingleton():尝试去缓存中获取对象;
Spring如何解决循环依赖
Spring为了解决单例的循环依赖问题,使用了三级缓存。三级缓存如下:
/** Cache of singleton objects: bean name --> bean instance */
/** 一级缓存 这个就是我们大名鼎鼎的单例缓存池 用于保存我们所有的单实例bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
/** 三级缓存 该map用户缓存key为beanName value为ObjectFactory(包装为早期对象) */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Cache of early singleton objects: bean name --> bean instance */
/** 二级缓存 用户缓存我们的key为beanName value是我们的早期对象(对象属性还没有来得及进行赋值) */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
原理分析:
所有的对象创建的过程中都会把自己的早期对象暴露到三级缓存中去。
给我们的早期对象属性赋值完成后,从三级缓存中拿到早期对象后,把早期对象放置到二级缓存并且ObjectFactory包装对象从三级缓存中删除掉。
创建对象实例完毕之后就会删除二级缓存 把单例对象存储到单例缓存池中。
Spring Bean 构造器的循环依赖存在的问题
通过构造函数实现循环依赖程序会抛异常,因为程序没有把早期对象放到三级缓存中就已经给属性赋值了。