Spring源码-IOC容器(六)-bean的循环依赖
2017-06-18 00:00
891 查看
Spring IOC容器 源码解析系列,建议大家按顺序阅读,欢迎讨论
(spring源码均为4.1.6.RELEASE版本)
Spring源码-IOC容器(一)-构建简单IOC容器
Spring源码-IOC容器(二)-Bean的定位解析注册
Spring源码-IOC容器(三)-GetBean
Spring源码-IOC容器(四)-FactoryBean
Spring源码-IOC容器(五)-Bean的初始化
Spring源码-IOC容器(六)-bean的循环依赖
Spring源码-IOC容器(七)-ApplicationContext
Spring源码-IOC容器(八)-NamespaceHandler与自定义xml
Spring源码-IOC容器(九)-Component-Scan源码解析
Spring源码-IOC容器(十)-@Autowired解析
不知道大家有没有想过这样一种情况,在Spring的配置中,存在两个bean A和bean B,A依赖于B,B依赖于A,即A和B相互依赖(引用),xml配置如下:
BeanA.java
BeanB.java
此时通过BeanFactory获取beanA,并调用print方法
输出结果为
可以看出beanA拿到了beanB的引用,beanB同时也拿到了beanA的引用。可见在Spring中是支持循环引用的,怎么实现的,有没有限制,我们再从源码来解析一下。
getBean方法首先会从缓存中查询是否存在创建好的单例
从以上的代码可以看出
只针对单例的bean,多例的后面讨论
默认的singletonObjects缓存不存在要get的beanName时,判断beanName是否正在创建中
从early缓存earlySingletonObjects中再查询,early缓存是用来缓存已实例化但未组装完成的bean
如果early缓存也不存在,从singletonFactories中查找是否有beanName对应的ObjectFactory对象工厂
如果对象工厂存在,则调用getObject方法拿到bean对象
将bean对象加入early缓存,并移除singletonFactories的对象工厂
上面最重要的就是singletonFactories何时放入了可以通过getObject获得bean对象的ObjectFactory。根据我们的猜测,应该会是bean对象实例化后,而属性注入之前。仔细寻找后发现,在AbstractAutowireCapableBeanFactory类的doCreateBean方法,也就是实际bean创建的方法中,执行完createBeanInstance实例化bean之后有一段代码:
当判断bean为单例且正在创建中,而Spring允许循环引用时,将能获得bean对象的引用的ObjectFactory添加到singletonFactories中,此时就与之前的getSingleton方法相呼应。而allowCircularReferences标识在spring中默认为true,但是也可以通过setAllowCircularReferences方法对AbstractAutowireCapableBeanFactory进行设置。
再来看下getObject方法中的getEarlyBeanReference方法。这里也设置了一个InstantiationAwareBeanPostProcessor后置处理器的扩展点,允许在对象返回之前修改甚至替换bean。
来梳理一下上面
实例化BeanA
将能获取BeanA对象的ObjectFactory添加到singletonFactories中
BeanA注入BeanB属性,调用getBean("beanB")方法
实例化BeanB
将能获取BeanB对象的ObjectFactory添加到singletonFactories中
BeanB注入BeanA属性,调用getBean("beanA")
从singletonFactories中获取ObjectFactory并调用getObject方法拿到beanA对象的引用
BeanB创建完成,注入到BeanA的beanB属性中
BeanA创建完成返回
上面我们了解了单例的bean循环引用的处理过程,那么多例的呢?其实我们可以按上面的思路来思考一下,单例bean的循环引用是因为每个对象都是固定的,只是提前暴露对象的引用,最终这个引用对应的对象是创建完成的。但是多例的情况下,每次getBean都会创建一个新的对象,那么应该引用哪一个对象呢,这本身就已经是矛盾的了。因而spring中对于多例之间相互引用是会提示错误的。
Error creating bean with name 'beanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
可见spring会认为多例之间的循环引用是无法解决的。
(spring源码均为4.1.6.RELEASE版本)
Spring源码-IOC容器(一)-构建简单IOC容器
Spring源码-IOC容器(二)-Bean的定位解析注册
Spring源码-IOC容器(三)-GetBean
Spring源码-IOC容器(四)-FactoryBean
Spring源码-IOC容器(五)-Bean的初始化
Spring源码-IOC容器(六)-bean的循环依赖
Spring源码-IOC容器(七)-ApplicationContext
Spring源码-IOC容器(八)-NamespaceHandler与自定义xml
Spring源码-IOC容器(九)-Component-Scan源码解析
Spring源码-IOC容器(十)-@Autowired解析
不知道大家有没有想过这样一种情况,在Spring的配置中,存在两个bean A和bean B,A依赖于B,B依赖于A,即A和B相互依赖(引用),xml配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="beanA" class="com.lntea.spring.demo.bean.BeanA"> <property name="beanB" ref="beanB"></property> </bean> <bean id="beanB" class="com.lntea.spring.demo.bean.BeanB"> <property name="beanA" ref="beanA"></property> </bean> </beans>
BeanA.java
package com.lntea.spring.demo.bean; public class BeanA { private BeanB beanB; public void print(){ System.out.println("beanB:" + beanB + " beanA:" + beanB.getBeanA()); } public BeanB getBeanB() { return beanB; } public void setBeanB(BeanB beanB) { this.beanB = beanB; } }
BeanB.java
package com.lntea.spring.demo.bean; public class BeanB { private BeanA beanA; public BeanA getBeanA() { return beanA; } public void setBeanA(BeanA beanA) { this.beanA = beanA; } }
此时通过BeanFactory获取beanA,并调用print方法
BeanA beanA = beanFactory.getBean("beanA",BeanA.class); beanA.print();
输出结果为
beanB:com.lntea.spring.demo.bean.BeanB@59a6e353 beanA:com.lntea.spring.demo.bean.BeanA@7a0ac6e3
可以看出beanA拿到了beanB的引用,beanB同时也拿到了beanA的引用。可见在Spring中是支持循环引用的,怎么实现的,有没有限制,我们再从源码来解析一下。
getBean方法首先会从缓存中查询是否存在创建好的单例
Object sharedInstance = getSingleton(beanName); public Object getSingleton(String beanName) { return getSingleton(beanName, true); } protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 查询缓存中是否有创建好的单例 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存不存在,判断是否正在创建中 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 加锁防止并发 synchronized (this.singletonObjects) { // 从earlySingletonObjects中查询是否有early缓存 singletonObject = this.earlySingletonObjects.get(beanName); // early缓存也不存在,且允许early引用 if (singletonObject == null && allowEarlyReference) { // 从单例工厂Map里查询beanName ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // singletonFactory存在,则调用getObject方法拿到单例对象 singletonObject = singletonFactory.getObject(); // 将单例对象添加到early缓存中 this.earlySingletonObjects.put(beanName, singletonObject); // 移除单例工厂中对应的singletonFactory this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
从以上的代码可以看出
只针对单例的bean,多例的后面讨论
默认的singletonObjects缓存不存在要get的beanName时,判断beanName是否正在创建中
从early缓存earlySingletonObjects中再查询,early缓存是用来缓存已实例化但未组装完成的bean
如果early缓存也不存在,从singletonFactories中查找是否有beanName对应的ObjectFactory对象工厂
如果对象工厂存在,则调用getObject方法拿到bean对象
将bean对象加入early缓存,并移除singletonFactories的对象工厂
上面最重要的就是singletonFactories何时放入了可以通过getObject获得bean对象的ObjectFactory。根据我们的猜测,应该会是bean对象实例化后,而属性注入之前。仔细寻找后发现,在AbstractAutowireCapableBeanFactory类的doCreateBean方法,也就是实际bean创建的方法中,执行完createBeanInstance实例化bean之后有一段代码:
// bean为单例且允许循环引用且正在创建中 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // 创建ObjectFactory并添加到singletonFactories中 addSingletonFactory(beanName, new ObjectFactory<Object>() { [@Override](https://my.oschina.net/u/1162528) public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { // 判断默认缓存中没有beanName if (!this.singletonObjects.containsKey(beanName)) { // 添加ObjectFactory到singletonFactories this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
当判断bean为单例且正在创建中,而Spring允许循环引用时,将能获得bean对象的引用的ObjectFactory添加到singletonFactories中,此时就与之前的getSingleton方法相呼应。而allowCircularReferences标识在spring中默认为true,但是也可以通过setAllowCircularReferences方法对AbstractAutowireCapableBeanFactory进行设置。
再来看下getObject方法中的getEarlyBeanReference方法。这里也设置了一个InstantiationAwareBeanPostProcessor后置处理器的扩展点,允许在对象返回之前修改甚至替换bean。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); if (exposedObject == null) { return exposedObject; } } } } return exposedObject; }
来梳理一下上面
getBean("beanA")的执行过程
实例化BeanA
将能获取BeanA对象的ObjectFactory添加到singletonFactories中
BeanA注入BeanB属性,调用getBean("beanB")方法
实例化BeanB
将能获取BeanB对象的ObjectFactory添加到singletonFactories中
BeanB注入BeanA属性,调用getBean("beanA")
从singletonFactories中获取ObjectFactory并调用getObject方法拿到beanA对象的引用
BeanB创建完成,注入到BeanA的beanB属性中
BeanA创建完成返回
上面我们了解了单例的bean循环引用的处理过程,那么多例的呢?其实我们可以按上面的思路来思考一下,单例bean的循环引用是因为每个对象都是固定的,只是提前暴露对象的引用,最终这个引用对应的对象是创建完成的。但是多例的情况下,每次getBean都会创建一个新的对象,那么应该引用哪一个对象呢,这本身就已经是矛盾的了。因而spring中对于多例之间相互引用是会提示错误的。
// 如果已经存在多例的对象在创建中,就会抛出异常 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
Error creating bean with name 'beanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
可见spring会认为多例之间的循环引用是无法解决的。
相关文章推荐
- 【Spring源码--IOC容器的实现】(六)Bean的依赖注入
- Spring应用、原理以及粗读源码系列(一)--框架总述、以Bean为核心的机制(IoC容器初始化以及依赖注入)
- Spring源码解析三:IOC容器的依赖注入
- 通过DefaultListableBeanFactory加载.xml配置文件学习Spring-IoC容器注册/加载bean的机制(源码走读)
- Spring源码 - 注册bean信息到ioc容器
- Spring源码学习之:模拟实现BeanFactory,从而说明IOC容器的大致原理
- spring beans源码解读之 ioc容器之始祖--DefaultListableBeanFactory
- Spring源码学习IOC(3):IoC容器载入Bean定义资源文件
- 第36天(就业班) spring引入、专业术语、spring六大模块、bean创建对象的细节、IOC容器、对象依赖关系、自动装配、注解方式
- spring beans源码解读之 ioc容器之始祖--DefaultListableBeanFactory
- Spring源码 - 注册bean信息到ioc容器
- Ioc容器BeanPostProcessor-Spring 源码(3)
- Spring IOC源码详解之容器依赖注入
- spring源码学习之路---IOC容器初始化要义之bean定义载入(五)
- Spring 源码阅读五 IOC容器初始化之bean定义载入
- spring 源码探索--单例bean解决循环依赖问题
- Spring源码分析1--IoC容器载入Bean定义资源
- 【Spring源码--IOC容器的实现】(五)Bean对象的创建
- spring源码学习之路---IOC容器初始化要义之bean定义载入
- Spring源码 - 注册bean信息到ioc容器