前言:在doCreateBean方法中还遗留一个问题没有分析:循环依赖。循环依赖在Spring中是非常重要的一个知识点,因此单独进行分析。
什么是循环依赖
循环依赖就是循环引用,两个或两个以上的bean互相引用对方,最终形成一个闭环。如A依赖B,B依赖C,C依赖A。如下图所示:
循环依赖其实就是一个死循环的过程,在初始化A的时候发现引用了B,则就会去初始化B,然后发现B又引用C,则又去初始化C,在初始化C的时候,再次发现C引用了A,则又去初始化A,这样就处于死循环,除非有终结条件。
Spring中循环依赖的场景有两种:
- 构造器循环依赖。
- setter循环依赖。
构造器循环依赖
表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常。
setter循环依赖
表示通过setter注入方式构成的循环依赖,对于setter注入造成的循环依赖,Spring只解决单例模式下的循环依赖,对于其他作用域的循环依赖,则抛出BeanCurrentlyInCreationException异常。为什么Spring只解决单例模式下的循环依赖呢,这里首先了解下Spring的几个作用域。
Spring的scope作用域
- singleton:默认的scope,每个scope是singleton的bean都会被定义一个单例对象,该对象的生命周期与Spring IOC容器一致的(但在第一次注入时才会创建)。
- prototype:如果为bean的作用域为prototype,则每次注入时都会创建一个新的对象。
- request:bean被定义为在每个HTTP请求 中创建一个单例对象,也就是说在单个请求中都会复用这个单例对象。
- session:bean被定义为在一个session的生命周期内创建一个单例对象。
- application:bean被定义为在ServletContext的生命周期中复用一个单例对象。
- websocket:bean被定义为在websocket的生命周期中复用一个单例对象。
从上面对Spring中scope作用域的介绍,也可大致了解为什么Spring中只解决单例模式下的循环依赖了,因为其他作用域对象的生命周期并不与Spring IOC容器一致,并且最主要的一点是Spring并不会对除了单例模式的bean做缓存,因此Spring只能解决单例模式下的循环依赖。
具体循环依赖解决流程
首先在看bean的加载的入口,在doGetBean方法中有如下代码:
首先会根据beanName从单例bean缓存中获取,如果不为空,则直接返回,前面已经分析了该方法,这里再次提出来。
1 // DefaultListableBeanFactory 2 protected Object getSingleton(String beanName, boolean allowEarlyReference) { 3 // 从单例缓存中加载Bean 4 Object singletonObject = this.singletonObjects.get(beanName); 5 // 缓存中bean为空,且当前bean正在创建 6 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 7 // 做同步 8 synchronized (this.singletonObjects) { 9 // 从earlySingletonObjects集合中获取 10 singletonObject = this.earlySingletonObjects.get(beanName); 11 // earlySingletonObjects集合中没有,其允许提前创建 12 if (singletonObject == null && allowEarlyReference) { 13 // 从singletonFactories中获取对应的ObjectFactory 14 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 15 if (singletonFactory != null) { 16 // 获取bean 17 singletonObject = singletonFactory.getObject(); 18 // 将bean添加到earlySingletonObjects集合中 19 this.earlySingletonObjects.put(beanName, singletonObject); 20 // 从singletonFactories中移除对应的 21 this.singletonFactories.remove(beanName); 22 } 23 } 24 } 25 } 26 return singletonObject; 27 }
分析:
这里注意isSingletonCurrentlyInCreation函数,判断当前bean是否正在被创建,这里就是判断beanName是否在singletonsCurrentlyInCreation集合中,那么正在被创建的bean是合适添加进去的呢?还是在doGetBean方法中
1 // DefaultSingletonBeanRegistry 2 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { 3 Assert.notNull(beanName, "Bean name must not be null"); 4 // 做同步 5 synchronized (this.singletonObjects) { 6 // 从缓存中检查一遍 7 // 因为singlton模式其实已经复用了创建的bean,所以该步骤必须检查 8 Object singletonObject = this.singletonObjects.get(beanName); 9 // 为空,开始进行加载 10 if (singletonObject == null) { 11 if (this.singletonsCurrentlyInDestruction) { 12 throw new BeanCreationNotAllowedException(beanName, 13 "Singleton bean creation not allowed while singletons of this factory are in destruction " + 14 "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); 15 } 16 if (logger.isDebugEnabled()) { 17 logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); 18 } 19 // 加载前置处理 其实就是打一个标记 20 beforeSingletonCreation(beanName); 21 // 首先将新的newSingleton设置为false 22 boolean newSingleton = false; 23 boolean recordSuppressedExceptions = (this.suppressedExceptions == null); 24 if (recordSuppressedExceptions) { 25 this.suppressedExceptions = new LinkedHashSet<>(); 26 } 27 try { 28 // 初始化bean 29 // 该过程其实是调用createBean()方法 这里是一个回调方法 30 singletonObject = singletonFactory.getObject(); 31 newSingleton = true; 32 } catch (IllegalStateException ex) { 33 // Has the singleton object implicitly appeared in the meantime -> 34 // if yes, proceed with it since the exception indicates that state. 35 singletonObject = this.singletonObjects.get(beanName); 36 if (singletonObject == null) { 37 throw ex; 38 } 39 } catch (BeanCreationException ex) { 40 if (recordSuppressedExceptions) { 41 for (Exception suppressedException : this.suppressedExceptions) { 42 ex.addRelatedCause(suppressedException); 43 } 44 } 45 throw ex; 46 } finally { 47 if (recordSuppressedExceptions) { 48 this.suppressedExceptions = null; 49 } 50 // 一堆异常处理后,进行后置处理 移除标志 51 afterSingletonCreation(beanName); 52 } 53 // 新的bean 加入缓存中 54 if (newSingleton) { 55 addSingleton(beanName, singletonObject); 56 } 57 } 58 return singletonObject; 59 }
分析:
注意第20行代码beforeSingletonCreation函数。
1 // DefaultSingletonBeanRegistry 2 protected void beforeSingletonCreation(String beanName) { 3 // 这里会添加到正在创建bean的集合中 4 // 注意第一个条件,如果存在,则为false,直接短路 5 // 只有当第一个条件不存在[false]时,才会去进行添加操作 6 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { 7 throw new BeanCurrentlyInCreationException(beanName); 8 } 9 }
分析:
这里就会将正在创建的beanName添加到singletonsCurrentlyInCreation,如果出现构造函数的循环依赖bean注入,则会在此处抛出BeanCurrentlyCreationException异常,具体可以跟着代码调试一把,就可以更清楚的知晓流程。
接下来继续回到单例模式的循环依赖中:
在加载bean时,首先从单例缓存中获取bean对象。
- 首先从单例缓存中获取bean对象,如果缓存中存在bean对象则直接返回(单例模式的bean在创建过程中会进行缓存[singletonObjects])。
- 如果缓存中bean对象为空,且当前bean正在创建,则从earlySingletonObjects中获取。
- 如果earlySingletonObjects集合中不存在,且允许提前创建bean,则从singletonFactories中获取单例工厂,若singleFactory不为空,则通过getObject方法获取bean,并将bean对象加入到earlySingletonObjects集合中,然后从singleFactory集合中移除对应的单例工厂对象。
注意这里涉及到三个集合:
- singletonObjects (一级)单例对象 Cache
- earlySingletonObjects (二级)提前曝光的单例对象 Cache
- singletonFactories (三级)单例对象工厂 Cache
1 /** 2 * Cache of singleton objects: bean name to bean instance. 3 * 存放的是单例 bean 的映射。 4 * <p> 5 * 对应关系为 bean name --> bean instance 6 */ 7 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); 8 9 /** 10 * Cache of singleton factories: bean name to ObjectFactory.<br/> 11 * 存放的是 ObjectFactory,可以理解为创建单例 bean 的 factory 。 12 * <p> 13 * 对应关系是 bean name --> ObjectFactory 14 */ 15 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); 16 17 /** 18 * Cache of early singleton objects: bean name to bean instance.<br/> 19 * 存放的是早期的 bean,对应关系也是 bean name --> bean instance。 20 * <p> 21 * 它与 {@link #singletonFactories} 区别在于 earlySingletonObjects 中存放的 bean 不一定是完整。 22 * <p> 23 * 从 {@link #getSingleton(String)} 方法中,我们可以了解,bean 在创建过程中就已经加入到 earlySingletonObjects 中了。 24 * 所以当在 bean 的创建过程中,就可以通过 getBean() 方法获取。 25 * <p> 26 * 这个 Map 也是【循环依赖】的关键所在。 27 */ 28 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
这三个缓存集合就是解决Spring中循环依赖的所在,具体流程:
- 首先从一级缓存singletonObjects中获取,如果为null,且当前bean正在被创建,则从二级缓存earlySingletonObjects中获取,如果还是为null,且允许singletonFactories通过getObject获取,则从三级缓存singletonFactories中获取,如果得到,则将其加入二级缓存earlySingletonObjects中,并从三级缓存singletonFactories中移除对应的工厂对象(因为单例模式的bean只会被创建一次),这样三级缓存就升级到二级缓存了,所以二级缓存存在的意义就是缓存三级缓存中ObjectFactory#getObject的执行结果,提前曝光单例Bean对象。
如果从单例缓存中得到bean对象,则会调用getObjectForBeanInstance方法进一步处理,因为从缓存中得到的bean是最原始的bean,并不一定是最终所需要的bean对象。
上面分析了从缓存中获取bean对象,但是缓存中的值是从什么地方添加进来的呢?如果你调试过源码会发现这样一段代码:
1 // AbstractAutowireCapableBeanFactory 2 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && 3 isSingletonCurrentlyInCreation(beanName));// 当前单例bean是否正在被创建 4 if (earlySingletonExposure) { 5 if (logger.isDebugEnabled()) { 6 logger.debug("Eagerly caching bean '" + beanName + 7 "' to allow for resolving potential circular references"); 8 } 9 // 提前将创建的bean实例加入到singletonFactories中 10 // 为了后期避免循环依赖 11 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 12 }
分析:
当一个bean满足三个条件,则会被加入到缓存中。三个条件如下:
- 单例。
- 运行提前暴露bean。
- 当前bean正在被创建中。
DefaultSingletonBeanRegistry#addSingletonFactroy
1 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { 2 Assert.notNull(singletonFactory, "Singleton factory must not be null"); 3 synchronized (this.singletonObjects) { 4 if (!this.singletonObjects.containsKey(beanName)) { 5 this.singletonFactories.put(beanName, singletonFactory); 6 this.earlySingletonObjects.remove(beanName); 7 this.registeredSingletons.add(beanName); 8 } 9 } 10 }
分析:
- 如果单例缓存中不存在beanName,则将singletonFactory进行缓存[singletonFactories集合],注意这里还有一个earlySingletonObjects.remove操作,该操作是删除二级缓存(提前曝光的单例对象bean),因为二级缓存是从三级缓存转换而来的,因此在对三级缓存[singletonFactories]进行缓存时,需要remove二级缓存。
- 这段代码发生在createBeanInstance方法之后,也就是说这个bean其实已经被创建出来了,但它还是不是很完美(没有进行属性填充和初始化),但是对于其他依赖它的对象而言已经足够了(可根据对象引用定位到堆中对象),能够被认出来。所以Spring解决setter构造依赖的关键,就是对bean进行提前曝光。
上面介绍的了三级缓存[singletonFactories]与二级缓存[earlySingletonObjects]的出处,接下来看一级缓存的出处:
在DefaultSingletonBeanRegistry#getSingleton函数中:
1 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { 2 Assert.notNull(beanName, "Bean name must not be null"); 3 // 做同步 4 synchronized (this.singletonObjects) { 5 // 从缓存中检查一遍 6 // 因为singlton模式其实已经复用了创建的bean,所以该步骤必须检查 7 Object singletonObject = this.singletonObjects.get(beanName); 8 // 为空,开始进行加载 9 if (singletonObject == null) { 10 if (this.singletonsCurrentlyInDestruction) { 11 throw new BeanCreationNotAllowedException(beanName, 12 "Singleton bean creation not allowed while singletons of this factory are in destruction " + 13 "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); 14 } 15 if (logger.isDebugEnabled()) { 16 logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); 17 } 18 // 加载前置处理 其实就是打一个标记 19 beforeSingletonCreation(beanName); 20 // 首先将新的newSingleton设置为false 21 boolean newSingleton = false; 22 boolean recordSuppressedExceptions = (this.suppressedExceptions == null); 23 if (recordSuppressedExceptions) { 24 this.suppressedExceptions = new LinkedHashSet<>(); 25 } 26 try { 27 // 初始化bean 28 // 该过程其实是调用createBean()方法 这里是一个回调方法 29 singletonObject = singletonFactory.getObject(); 30 newSingleton = true; 31 } catch (IllegalStateException ex) { 32 // Has the singleton object implicitly appeared in the meantime -> 33 // if yes, proceed with it since the exception indicates that state. 34 singletonObject = this.singletonObjects.get(beanName); 35 if (singletonObject == null) { 36 throw ex; 37 } 38 } catch (BeanCreationException ex) { 39 if (recordSuppressedExceptions) { 40 for (Exception suppressedException : this.suppressedExceptions) { 41 ex.addRelatedCause(suppressedException); 42 } 43 } 44 throw ex; 45 } finally { 46 if (recordSuppressedExceptions) { 47 this.suppressedExceptions = null; 48 } 49 // 一堆异常处理后,进行后置处理 移除标志 50 afterSingletonCreation(beanName); 51 } 52 // 新的bean 加入缓存中 53 if (newSingleton) { 54 addSingleton(beanName, singletonObject); 55 } 56 } 57 return singletonObject; 58 } 59 }
分析:
这里关注第54行处:addSingleton函数。
1 // DefaultSingletonBeanRegistry 2 protected void addSingleton(String beanName, Object singletonObject) { 3 synchronized (this.singletonObjects) { 4 this.singletonObjects.put(beanName, singletonObject); 5 this.singletonFactories.remove(beanName); 6 this.earlySingletonObjects.remove(beanName); 7 this.registeredSingletons.add(beanName); 8 } 9 }
分析:
这里就是将bean加入一级缓存中[singletonObjects],同时remove二级缓存和三级缓存中值,因为bean已经被创建成功了,二级缓存与三级缓存也就不需要了。
总结
从上面分析过程可以知道为什么Spring只解决单例模式下的循环依赖了吧?
- Spring只对单例模式的bean进行了提前暴光[singletonFactories],这也是由于其他scope域bean的特性所致。
单例模式下循环依赖解决流程:
- 首先A完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),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完成初始化。到这里整个链路就已经完成了初始化过程了。
如果你看过《Spring源码深度解析》这本书,还能发现如下流程图:
注:看图说话,笔者最为喜欢!其实去亲身调试一下循环依赖的代码,可能会有更加深刻的认识。
by Shawn Chen,2019.04.29,下午。