Spring源码学习--Bean对象循环依赖问题解决(四)
2017-07-05 18:28
1031 查看
循环依赖就是N个类相互嵌套引用,如果通过new对象的方式产生循环依赖的话会导致程序内存溢出报错,接下来我们了解一下spring是如何解决循环依赖问题。
第一种:prototype原型bean循环依赖
对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx方法产生循环依赖,Spring都会直接报错处理。
AbstractBeanFactory.doGetBean()方法:
1、Spring创建ClassA时首先会调用beforeSingletonCreation(beanName),判断单例bean是否正在被创建,如果正在被创建则报错,如果没有被创建则做标记
2、Spring从容器中无法获取ClassB的实例,接下来Spring按照步骤1创建ClassB的实例,在构造ClassB的构造函数时需要用到ClassA的实例。
3、Spring还是首先尝试从容器中获取ClassA的实例,由于第1步还没有执行结束,此时还没有ClassA实例,Spring容器认为就需要初始化ClassA了,重复第1步,
但是ClassA一开始就已经进行了第1步,并且做标记bean正在创建,当再次进入第一步时就会抛出异常错误。
总结:Spring在创建构造器循环依赖时其实就是循环初始化操作 A-> B -> A 当A要被初始化第二次时就直接抛出异常。第三种 :单例bean通过setXxx或者@Autowired进行循环依赖
Spring通过setXxx或者@Autowired方法解决循环依赖其实是通过提前暴露一个ObjectFactory对象来完成的,简单来说ClassA在调用构造器完成对象初始化之后,在调用ClassA的setClassB方法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。
1、Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器。
3、Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
4、ClassB调用setClassA方法,Spring从容器中获取ClassA ,因为第一步中已经提前暴露了ClassA,因此可以获取到ClassA实例
5、ClassA通过spring容器获取到ClassB,完成了对象初始化操作。
6、这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。
总结:简单来说就是对象通过构造函数初始化之后就暴露到容器中,这样就不会存在循环初始化对象的情况了。
第一种:prototype原型bean循环依赖
对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx方法产生循环依赖,Spring都会直接报错处理。AbstractBeanFactory.doGetBean()方法:
if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
protected boolean isPrototypeCurrentlyInCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); }在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进行标记这个beanName正在被创建,等创建结束之后会删除标记
try { //创建原型bean之前添加标记 beforePrototypeCreation(beanName); //创建原型bean prototypeInstance = createBean(beanName, mbd, args); } finally { //创建原型bean之后删除标记 afterPrototypeCreation(beanName); }总结:简单来说Spring不支持原型bean的循环依赖。
第二种:单例bean 构造器参数循环依赖
对于单例bean通过构造器参数进行循环依赖时,Spring也是通过抛出异常进行处理的,接下来我们分析一下其是如何实现。假设这里有两个类ClassA 和 ClassB,两个类通过构造器进行循环依赖。1、Spring创建ClassA时首先会调用beforeSingletonCreation(beanName),判断单例bean是否正在被创建,如果正在被创建则报错,如果没有被创建则做标记
protected void beforeSingletonCreation(String beanName) { //singletonsCurrentlyInCreation是一个集合,不可重复,add返回true则表明没有创建,返回false则说明bean在创建 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }此时刚开始创建ClassA,因此不会报异常错误,当初始化ClassA的构造器时需要用到ClassB的实例。
2、Spring从容器中无法获取ClassB的实例,接下来Spring按照步骤1创建ClassB的实例,在构造ClassB的构造函数时需要用到ClassA的实例。
3、Spring还是首先尝试从容器中获取ClassA的实例,由于第1步还没有执行结束,此时还没有ClassA实例,Spring容器认为就需要初始化ClassA了,重复第1步,
但是ClassA一开始就已经进行了第1步,并且做标记bean正在创建,当再次进入第一步时就会抛出异常错误。
总结:Spring在创建构造器循环依赖时其实就是循环初始化操作 A-> B -> A 当A要被初始化第二次时就直接抛出异常。
第三种 :单例bean通过setXxx或者@Autowired进行循环依赖
Spring通过setXxx或者@Autowired方法解决循环依赖其实是通过提前暴露一个ObjectFactory对象来完成的,简单来说ClassA在调用构造器完成对象初始化之后,在调用ClassA的setClassB方法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。1、Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器。
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对象注入到容器中 addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); }2、ClassA调用setClassB方法,Spring首先尝试从容器中获取ClassB,此时ClassB不存在Spring容器中。
3、Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
4、ClassB调用setClassA方法,Spring从容器中获取ClassA ,因为第一步中已经提前暴露了ClassA,因此可以获取到ClassA实例
5、ClassA通过spring容器获取到ClassB,完成了对象初始化操作。
6、这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。
总结:简单来说就是对象通过构造函数初始化之后就暴露到容器中,这样就不会存在循环初始化对象的情况了。
相关文章推荐
- spring 源码探索--单例bean解决循环依赖问题
- Spring源码-IOC容器(六)-bean的循环依赖
- 解决Spring中singleton的Bean依赖于prototype的Bean的问题
- Spring - Bean 循环依赖问题
- Spring学习07--Bean对象的初始化(IOC的依赖注入)原理
- Spring中的循环依赖问题介绍及解决方法
- extjs解决bean转换为json对象的死循环的问题
- 解决Spring中singleton的Bean依赖于prototype的Bean的问题
- Spring 4.2 方法注入解决单例Bean的原型Bean依赖问题
- Spring源码学习--Bean对象变量初始化(三)
- Spring源码学习--Bean对象初始化(二)
- 从spring源码角度分析循环依赖bean的组装
- Spring中的循环依赖问题介绍及解决方法
- 转--解决Spring中singleton的Bean依赖于prototype的Bean的问题
- Spring源码解析笔记5——循环依赖的解决
- 详解Spring-bean的循环依赖以及解决方式
- Spring源码阅读-BeanFactory-循环依赖
- 解决spring循环依赖的问题:has been injected into other beans
- 解决spring循环依赖的问题:has been injected into other beans