在上篇文章中,咱们详细分析了
doCreateBean()
中的第2步:实例化bean,本文接着分析doCreateBean()
的第4步“循环依赖处理”,也就是populateBean()
方法。java
首先回顾下Bean加载的主流程:缓存
createBeanInstance()
实例化 bean本章咱们主要分析第4步:app
循环依赖,其实就是循环引用,就是两个或者两个以上的 bean 互相引用对方,最终造成一个闭环,如 A 依赖 B,B 依赖 C,C 依赖 A。以下图所示:源码分析
Spring中的循环依赖,其实就是一个死循环的过程,在初始化 A 的时候发现依赖了 B,这时就会去初始化 B,而后又发现 B 依赖 C,跑去初始化 C,初始化 C 的时候发现依赖了 A,则又会去初始化 A,依次循环永不退出,除非有终结条件。post
通常来讲,Spring 循环依赖的状况有两种:this
构造器的循环依赖。 field 属性的循环依赖。 对于构造器的循环依赖,Spring 是没法解决的,只能抛出 BeanCurrentlyInCreationException
异常表示循环依赖,因此下面咱们分析的都是基于 field 属性的循环依赖。prototype
在前文 Spring Ioc源码分析 之 Bean的加载(三):各个 scope 的 Bean 建立 中提到,Spring 只解决 scope 为 singleton 的循环依赖。对于scope 为 prototype 的 bean ,Spring 没法解决,直接抛出 BeanCurrentlyInCreationException 异常。code
为何 Spring 不处理 prototype bean 呢?其实若是理解 Spring 是如何解决 singleton bean 的循环依赖就明白了。这里先留个疑问,咱们先来看下 Spring 是如何解决 singleton bean 的循环依赖的。对象
在AbstractBeanFactory 的 doGetBean()
方法中,咱们根据BeanName去获取Singleton Bean的时候,会先从缓存获取。
代码以下:blog
//DefaultSingletonBeanRegistry.java @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 从一级缓存缓存 singletonObjects 中加载 bean Object singletonObject = this.singletonObjects.get(beanName); // 缓存中的 bean 为空,且当前 bean 正在建立 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 加锁 synchronized (this.singletonObjects) { // 从 二级缓存 earlySingletonObjects 中获取 singletonObject = this.earlySingletonObjects.get(beanName); // earlySingletonObjects 中没有,且容许提早建立 if (singletonObject == null && allowEarlyReference) { // 从 三级缓存 singletonFactories 中获取对应的 ObjectFactory ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //从单例工厂中获取bean singletonObject = singletonFactory.getObject(); // 添加到二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); // 从三级缓存中删除 this.singletonFactories.remove(beanName); } } } } return singletonObject; }
这段代码涉及的3个关键的变量,分别是3个级别的缓存,定义以下:
/** Cache of singleton objects: bean name --> bean instance */ //单例bean的缓存 一级缓存 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name --> ObjectFactory */ //单例对象工厂缓存 三级缓存 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name --> bean instance */ //预加载单例bean缓存 二级缓存 //存放的 bean 不必定是完整的 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
getSingleton()
的逻辑比较清晰:
首先,尝试从一级缓存singletonObjects
中获取单例Bean。
若是获取不到,则从二级缓存earlySingletonObjects
中获取单例Bean。
若是仍然获取不到,则从三级缓存singletonFactories
中获取单例BeanFactory。
最后,若是从三级缓存中拿到了BeanFactory,则经过getObject()
把Bean存入二级缓存中,并把该Bean的三级缓存删掉。
看到这里可能会有些疑问,这3个缓存怎么就解决了singleton循环依赖了呢?
先别着急,咱们如今分析了获取缓存的代码,再来看下存储缓存的代码。 在 AbstractAutowireCapableBeanFactory 的 doCreateBean()
方法中,有这么一段代码:
// AbstractAutowireCapableBeanFactory.java boolean earlySingletonExposure = (mbd.isSingleton() // 单例模式 && this.allowCircularReferences // 容许循环依赖 && isSingletonCurrentlyInCreation(beanName)); // 当前单例 bean 是否正在被建立 if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // 为了后期避免循环依赖,提早将建立的 bean 实例加入到三级缓存 singletonFactories 中 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
这段代码就是put三级缓存singletonFactories
的地方,其核心逻辑是,当知足如下3个条件时,把bean加入三级缓存中:
addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)
方法,代码以下:
// DefaultSingletonBeanRegistry.java protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
从这段代码咱们能够看出,singletonFactories 这个三级缓存才是解决 Spring Bean 循环依赖的关键。同时这段代码发生在 createBeanInstance(...)
方法以后,也就是说这个 bean 其实已经被建立出来了,可是它尚未完善(没有进行属性填充和初始化),可是对于其余依赖它的对象而言已经足够了(已经有内存地址了,能够根据对象引用定位到堆中对象),可以被认出来了。
到这里咱们发现三级缓存 singletonFactories 和 二级缓存 earlySingletonObjects 中的值都有出处了,那一级缓存在哪里设置的呢?在类 DefaultSingletonBeanRegistry 中,能够发现这个 addSingleton(String beanName, Object singletonObject)
方法,代码以下:
// DefaultSingletonBeanRegistry.java 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); } }
该方法是在 #doGetBean(...) 方法中,处理不一样 scope 时,若是是 singleton调用的,以下图所示:
也就是说,一级缓存里面是完整的Bean。
小结:
getObject()
方法从三级缓存的BeanFactory中取出Bean总结
如今咱们再来回顾下Spring解决单例循环依赖的方案:
Spring 在建立 bean 的时候并非等它彻底完成,而是在建立过程当中将建立中的 bean 的 ObjectFactory 提早曝光(即加入到 singletonFactories 三级缓存中)。
这样,一旦下一个 bean 建立的时候须要依赖 bean ,则从三级缓存中获取。
举个栗子:
好比咱们团队里要报名参加活动,你不用上来就把你的生日、性别、家庭信息什么的所有填完,你只要先报个名字,统计下人数就行,以后再慢慢完善你的我的信息。
核心思想:提早暴露,先用着
最后来描述下就上面那个循环依赖 Spring 解决的过程:
首先 A 完成初始化第一步并将本身提早曝光出来(经过 三级缓存 将本身提早曝光),在初始化的时候,发现本身依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 尚未被建立出来
而后 B 就走建立流程,在 B 初始化的时候,一样发现本身依赖 C,C 也没有被建立出来
这个时候 C 又开始初始化进程,可是在初始化的过程当中发现本身依赖 A,因而尝试 get(A),这个时候因为 A 已经添加至缓存中(三级缓存 singletonFactories ),经过 ObjectFactory 提早曝光,因此能够经过 ObjectFactory#getObject()
方法来拿到 A 对象,C 拿到 A 对象后顺利完成初始化,而后将本身添加到一级缓存中
回到 B ,B 也能够拿到 C 对象,完成初始化,A 能够顺利拿到 B 完成初始化。到这里整个链路就已经完成了初始化过程了
最后,为何多例模式不能解决循环依赖呢?
由于多例模式下每次new() Bean都不是一个,若是按照这样存到缓存中,就变成单例了。
参考:
http://cmsblogs.com/?p=todo (小明哥)