什么是bean的循环依赖
a对象中有b属性。b对象中有a属性。这就是循环依赖。我依赖你,你也依赖我。
比如:丈夫类husband,妻子类wife。husband中有wife的引用。wife中有husband的引用。

package com.powernode.spring6.bean;
/**
* @author 动力节点
* @version 1.0
* @classname husband
* @since 1.0
**/
public class husband {
private string name;
private wife wife;
}package com.powernode.spring6.bean;
/**
* @author 动力节点
* @version 1.0
* @classname wife
* @since 1.0
**/
public class wife {
private string name;
private husband husband;
}singleton下的set注入产生的循环依赖
我们来编写程序,测试一下在singleton+setter的模式下产生的循环依赖,spring是否能够解决?
package com.powernode.spring6.bean;
/**
* @author 动力节点
* @version 1.0
* @classname husband
* @since 1.0
**/
public class husband {
private string name;
private wife wife;
public void setname(string name) {
this.name = name;
}
public string getname() {
return name;
}
public void setwife(wife wife) {
this.wife = wife;
}
// tostring()方法重写时需要注意:不能直接输出wife,输出wife.getname()。要不然会出现递归导致的栈内存溢出错误。
@override
public string tostring() {
return "husband{" +
"name='" + name + '\'' +
", wife=" + wife.getname() +
'}';
}
}package com.powernode.spring6.bean;
/**
* @author 动力节点
* @version 1.0
* @classname wife
* @since 1.0
**/
public class wife {
private string name;
private husband husband;
public void setname(string name) {
this.name = name;
}
public string getname() {
return name;
}
public void sethusband(husband husband) {
this.husband = husband;
}
// tostring()方法重写时需要注意:不能直接输出husband,输出husband.getname()。要不然会出现递归导致的栈内存溢出错误。
@override
public string tostring() {
return "wife{" +
"name='" + name + '\'' +
", husband=" + husband.getname() +
'}';
}
}<?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.xsd">
<bean id="husbandbean" class="com.powernode.spring6.bean.husband" scope="singleton">
<property name="name" value="张三"/>
<property name="wife" ref="wifebean"/>
</bean>
<bean id="wifebean" class="com.powernode.spring6.bean.wife" scope="singleton">
<property name="name" value="小花"/>
<property name="husband" ref="husbandbean"/>
</bean>
</beans>package com.powernode.spring6.test;
import com.powernode.spring6.bean.husband;
import com.powernode.spring6.bean.wife;
import org.junit.test;
import org.springframework.context.applicationcontext;
import org.springframework.context.support.classpathxmlapplicationcontext;
/**
* @author 动力节点
* @version 1.0
* @classname circulardependencytest
* @since 1.0
**/
public class circulardependencytest {
@test
public void testsingletonandset(){
applicationcontext applicationcontext = new classpathxmlapplicationcontext("spring.xml");
husband husbandbean = applicationcontext.getbean("husbandbean", husband.class);
wife wifebean = applicationcontext.getbean("wifebean", wife.class);
system.out.println(husbandbean);
system.out.println(wifebean);
}
}通过测试得知:在singleton + set注入的情况下,循环依赖是没有问题的。spring可以解决这个问题。
spring解决循环依赖的机理
spring为什么可以解决set + singleton模式下循环依赖?
根本的原因在于:这种方式可以做到将“实例化bean”和“给bean属性赋值”这两个动作分开去完成。
实例化bean的时候:调用无参数构造方法来完成。此时可以先不给属性赋值,可以提前将该bean对象“曝光”给外界。
给bean属性赋值的时候:调用setter方法来完成。
两个步骤是完全可以分离开去完成的,并且这两步不要求在同一个时间点上完成。
也就是说,bean都是单例的,我们可以先把所有的单例bean实例化出来,放到一个集合当中(我们可以称之为缓存),所有的单例bean全部实例化完成之后,以后我们再慢慢的调用setter方法给属性赋值。这样就解决了循环依赖的问题。
那么在spring框架底层源码级别上是如何实现的呢?请看:

在以上类中包含三个重要的属性:
**cache of singleton objects: bean name to bean instance. **单例对象的缓存:key存储bean名称,value存储bean对象【一级缓存】
**cache of early singleton objects: bean name to bean instance. **早期单例对象的缓存:key存储bean名称,value存储早期的bean对象【二级缓存】
**cache of singleton factories: bean name to objectfactory. **单例工厂缓存:key存储bean名称,value存储该bean对应的objectfactory对象【三级缓存】
这三个缓存其实本质上是三个map集合。
我们再来看,在该类中有这样一个方法addsingletonfactory(),这个方法的作用是:将创建bean对象的objectfactory对象提前曝光。

再分析下面的源码:

从源码中可以看到,spring会先从一级缓存中获取bean,如果获取不到,则从二级缓存中获取bean,如果二级缓存还是获取不到,则从三级缓存中获取之前曝光的objectfactory对象,通过objectfactory对象获取bean实例,这样就解决了循环依赖的问题。
总结:
spring只能解决setter方法注入的单例bean之间的循环依赖。classa依赖classb,classb又依赖classa,形成依赖闭环。spring在创建classa对象后,不需要等给属性赋值,直接将其曝光到bean缓存当中。在解析classa的属性时,又发现依赖于classb,再次去获取classb,当解析classb的属性时,又发现需要classa的属性,但此时的classa已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的classa的实例,直接从缓存中获取即可。从而解决循环依赖问题。
到此这篇关于bean的循环依赖问题的文章就介绍到这了,更多相关bean循环依赖内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论