Spring中的循环依赖问题
2016-06-23 16:44
686 查看
最近在研究Spring IOC容器,遇到了对象的循环依赖问题,通过看源码才明白Spring是如何优雅的解决单例循环依赖问题。Spring将对象依赖分成属性依赖和构造依赖,构造依赖问题无法解决,只能抛出BeanCurrentlyInCreationException异常,在解决属性依赖问题,Spring采用的是提前暴露对象的方法。
构造依赖问题:
首先上代码:
有一个类Office
以上两个类存在构造依赖问题,bean.xml配置如下:
当采用Spring初始时,会抛出异常。我们接下来看看Spring是怎么发现这个异常的。
首先Spring需要预初始化单例对象office,在创建之前,会将office放入到singletonsCurrentlyInCreation集合中,在创建office对象时,发现它有个参数car是引用类型,因此Spring会通过getBean(“car”)去获得car对象,由于car对象尚未创建,则创建car对象,在创建car对象之前,也会将car放入到singletonsCurrentlyInCreation中,实例化car对象时,car构造依赖office,同样也会通过getBean(“office”),由于上一个office对象还未创建成功,这个时候Spring需再重新创建office对象,在创建之前,将office放入到singletonsCurrentlyInCreation时,发现office在singletonsCurrentlyInCreation集合中已经存在了,这个时候Spring判断对象之间出现了循环依赖,那么抛出BeanCurrentlyInCreationException异常。
属性依赖问题:
先上代码:
下面是bean.xml配置:
大家可以看到,这个时候office和boss类也存在循环依赖关系,但这时候office采用默认构造函数,不依赖于boss对象,这种属于属性依赖,属性依赖Spring可以很好的解决,采用我们上文提到的提前暴露对象。下面分析具体的处理过程。
首先Spring创建office对象,采用其默认构造函数,创建成功后,Spring会通过以下代码将对象提前暴露出来,尽管此时的对象还未完成属性注入,属于早期对象。这个时候对象放在singletonFactories的Map表中,value是函数对象ObjectFactory.
接下来,Spring会通过函数populateBean来完成office对象的属性注入,再注入boss属性时,发现是一个引用对象,这个时候同样会通过getBean(“boss”)来获得boss对象,boss对象由于从未创建,则创建boss对象。在创建boss对象时,发现它构造依赖于office对象,这个时候Spring也会通过getBean(“office”)获取office对象。由于存在office提前暴露出来,这个时候直接从singletonFactories的Map表中得到office对象并返回,并不需要重新再创建office对象,这样就避免了循环依赖问题,接下来boss对象可以成功被创建,则返回到到office的属性注入中。office属性注入完成后,得到的office对象是成型的,接下来Spring会进一步判断office在后期的处理过程中是否发生引用更改,所谓引用更改就是成型的office对象与早期暴露的office对象是否还是同一个对象。下面是验证代码。
如果是同一个引用对象,则循环引用成立,否则会抛出BeanCurrentlyInCreationException异常,大家可看到异常消息:
Bean with name ’ beanName ’ has been injected into other beans in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using ‘getBeanNamesOfType’ with the ‘allowEagerInit’ flag turned off, for example.
大意是:当前对象的早期版本被注入到其他对象引用中,也就是最终版本和原始版本不一样导致的,这个时候Spring只能抛出异常。
好了,Spring的循环依赖处理过程就这些了,如果有什么错误,欢迎指正。
构造依赖问题:
首先上代码:
有一个类Office
public class Office { public String name; public Boss boss; public Office(String name,Boss boss){ this.name=name; this.boss=boss; } public Boss getBoss() { return boss; } public void setBoss(Boss boss) { this.boss = boss; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class Boss { private String name; private Car car; private Office office; /*public Boss(){ }*/ public Boss(String name,Office office){ this.name=name; this.Office=office; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } public Office getOffice() { return office; } public void setOffice(Office office) { this.office = office; } }
以上两个类存在构造依赖问题,bean.xml配置如下:
<bean id="office" class="org.lww.springTest.Office"> <constructor-arg index="0" value="400工作室"></constructor-arg> <constructor-arg index="1" ref="boss"></constructor-arg> </bean> <bean id="boss" class="org.lww.springTest.Boss"> <constructor-arg index="0" value="Liweiwei"></constructor-arg> <constructor-arg index="1" ref="office"></constructor-arg> </bean>
当采用Spring初始时,会抛出异常。我们接下来看看Spring是怎么发现这个异常的。
首先Spring需要预初始化单例对象office,在创建之前,会将office放入到singletonsCurrentlyInCreation集合中,在创建office对象时,发现它有个参数car是引用类型,因此Spring会通过getBean(“car”)去获得car对象,由于car对象尚未创建,则创建car对象,在创建car对象之前,也会将car放入到singletonsCurrentlyInCreation中,实例化car对象时,car构造依赖office,同样也会通过getBean(“office”),由于上一个office对象还未创建成功,这个时候Spring需再重新创建office对象,在创建之前,将office放入到singletonsCurrentlyInCreation时,发现office在singletonsCurrentlyInCreation集合中已经存在了,这个时候Spring判断对象之间出现了循环依赖,那么抛出BeanCurrentlyInCreationException异常。
属性依赖问题:
先上代码:
public class Office { public String name; public Boss boss; public Office(){ } public Office(String name,Boss boss){ this.name=name; this.boss=boss; } public Boss getBoss() { return boss; } public void setBoss(Boss boss) { this.boss = boss; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class Boss { private String name; private Car car; private Office office; /*public Boss(){ }*/ public Boss(String name,Office office){ this.name=name; this.office=office; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } public Office getOffice() { return office; } public void setOffice(Office office) { this.office = office; } }
下面是bean.xml配置:
<bean id="office" class="org.lww.springTest.Office"> <property name="name" value="400工作室"></property> <property name="boss" ref="boss"></property> </bean> <bean id="boss" class="org.lww.springTest.Boss"> <constructor-arg index="0" value="Liweiwei"></constructor-arg> <constructor-arg index="1" ref="office"></constructor-arg> </bean>
大家可以看到,这个时候office和boss类也存在循环依赖关系,但这时候office采用默认构造函数,不依赖于boss对象,这种属于属性依赖,属性依赖Spring可以很好的解决,采用我们上文提到的提前暴露对象。下面分析具体的处理过程。
首先Spring创建office对象,采用其默认构造函数,创建成功后,Spring会通过以下代码将对象提前暴露出来,尽管此时的对象还未完成属性注入,属于早期对象。这个时候对象放在singletonFactories的Map表中,value是函数对象ObjectFactory.
addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } });
接下来,Spring会通过函数populateBean来完成office对象的属性注入,再注入boss属性时,发现是一个引用对象,这个时候同样会通过getBean(“boss”)来获得boss对象,boss对象由于从未创建,则创建boss对象。在创建boss对象时,发现它构造依赖于office对象,这个时候Spring也会通过getBean(“office”)获取office对象。由于存在office提前暴露出来,这个时候直接从singletonFactories的Map表中得到office对象并返回,并不需要重新再创建office对象,这样就避免了循环依赖问题,接下来boss对象可以成功被创建,则返回到到office的属性注入中。office属性注入完成后,得到的office对象是成型的,接下来Spring会进一步判断office在后期的处理过程中是否发生引用更改,所谓引用更改就是成型的office对象与早期暴露的office对象是否还是同一个对象。下面是验证代码。
if (earlySingletonExposure) { //获取指定名称的已注册的单态模式Bean对象 // allowEarlyReference为false,则不会解析singletonFactories Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) {//判断点1:首先确定这个对象能从earlySingletonObjects中取出对象来 //根据名称获取的以注册的Bean和正在实例化的Bean是同一个 if (exposedObject == bean) { //再判断这个对象和当前通过beanPostProcessor处理过的对象是否相同,如果相同,表示对象没有经过修改,即A=A-,那么循环引用成立。无需处理 exposedObject = earlySingletonReference; } //当前Bean依赖其他Bean,并且当发生循环引用时不允许新创建实例对象 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) {//出现循环引用,且被引用的bean被修改 throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } }
如果是同一个引用对象,则循环引用成立,否则会抛出BeanCurrentlyInCreationException异常,大家可看到异常消息:
Bean with name ’ beanName ’ has been injected into other beans in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using ‘getBeanNamesOfType’ with the ‘allowEagerInit’ flag turned off, for example.
大意是:当前对象的早期版本被注入到其他对象引用中,也就是最终版本和原始版本不一样导致的,这个时候Spring只能抛出异常。
好了,Spring的循环依赖处理过程就这些了,如果有什么错误,欢迎指正。
相关文章推荐
- 解决Maven项目相互依赖/循环依赖/双向依赖的问题
- Spring3.1.0实现原理分析(五).关于循环引用的探讨
- spring 循环依赖注入
- Spring3.1.0实现原理分析(五).关于循环引用的探讨
- Spring-bean的循环依赖以及解决方式
- Spring循环依赖
- Maven项目如果涉及多个模块的话,设计的时候就要考虑模块循环依赖的问题
- 一场maven循环依赖引发的大案!
- [AngularJS面面观] 16. 依赖注入 --- 注入器中如何管理对象
- spring 源码-循环依赖
- Maven Ban Circular Dependencies
- 使用 IDEA 解决 Maven 项目循环依赖
- Spring——bean的加载
- JAVA,循环依赖,Spring
- springboot的@Async循环依赖问题
- Spring循环依赖
- JAVA中循环依赖的相关问题
- Spring源码解析:循环依赖的探测与处理
- 从spring源码角度分析循环依赖bean的组装
- Spring循环依赖正确性及Bean注入的顺序关系详解