什么是循环依赖?
有以下三种依赖情况
虽然方式不一样但是,依赖的本质是一样的,就是你完整的创建需要依赖我,我的完整也要依赖你,最终谁都都无法构建,造成构建失败
产生循环依赖的情况
/*
*原因在创建A的过程中创建了B,创建B又需要创建A,而此时A还未创建完成,
*/
public class CircularTest {
public static void main(String[] args) {
//造成了循环依赖问题,
System.out.println(new CircularA());
}
}
class CircularA{
//在A中依赖B
CircularB b = new CircularB();
}
class CircularB{
//在B中依赖A
CircularA a = new CircularA();
}
这样就产生了,栈溢出
解决循环依赖
上面的主要问题就是,在B创建A的时候,拿不到A的实例,解决了这步,就是就解决了这个问题,也就是将创建过程分开,1、构造方法实例 2、成员变量赋值两个阶段
如下图
开始改代码
public class CircularTest {
public static void main(String[] args) {
//造成了循环依赖问题,
System.out.println(new CircularA());
}
}
class CircularA {
//在A中依赖B
CircularB b;
public CircularB getB() {
return b;
}
public void setB(CircularB b) {
this.b = b;
}
}
class CircularB {
//在B中依赖A
CircularA a;
public CircularA getA() {
return a;
}
public void setA(CircularA a) {
this.a = a;
}
}
在这中情况下,肯定没问题
但是此时还没有赋值,下面进行赋值
public class CircularTest {
public static void main(String[] args) throws Exception {
//造成了循环依赖问题,
System.out.println(getBean(CircularA.class));
}
public static <T> T getBean(Class<T> className) throws Exception{
//无参构造赋值
Object obj = className.newInstance();
//成员变量赋值
Field[] declaredFields = className.getDeclaredFields();//获得所有变量
for (Field field : declaredFields) {
field.setAccessible(true);//暴力反射赋值
Class<?> fieldType = field.getType();//获得属性变量类型,返回class文件
field.set(obj,getBean(fieldType));//这就是循环依赖发生的地方,这里存放对象实例B
}
return (T) obj;
}
}
class CircularA {
//在A中依赖B
CircularB b;
public CircularB getB() {
return b;
}
public void setB(CircularB b) {
this.b = b;
}
}
class CircularB {
//在B中依赖A
CircularA a;
public CircularA getA() {
return a;
}
public void setA(CircularA a) {
this.a = a;
}
}
但是此时仍然出现循环依赖问题,我们虽然是分成了两部,但是 field.set(obj,getBean(fieldType))这步仍然是循环依赖。
这时我们提前暴露对象即可,我可以利用,map存储利用容器提前暴露半成品对象,撸代码
public class CircularTest {
private static final Map<String,Object> map = new HashMap<>();//创建map用于存储对象提前暴露
public static void main(String[] args) throws Exception {
//造成了循环依赖问题,
System.out.println(getBean(CircularA.class));
System.out.println(getBean(CircularB.class));
}
public static <T> T getBean(Class<T> className) throws Exception{
//如果Map有对象,那么直接在map中取到实例,并返回,这也保证了单例模式
if(map.containsKey(className.getName()))return (T) map.get(className.getName());
//如果没有我们就创建半成品对象在map中
//无参构造半程品
Object obj = className.newInstance();
//将半成品放到map容器中
map.put(className.getName(),obj);
//成员变量赋值
Field[] declaredFields = className.getDeclaredFields();//获得所有变量
for (Field field : declaredFields) {
field.setAccessible(true);//暴力反射赋值
Class<?> fieldType = field.getType();//获得属性变量类型,返回class文件
field.set(obj,getBean(fieldType));//这就是循环依赖发生的地方,这里存放对象实例B
}
return (T) obj;
}
}
class CircularA {
//在A中依赖B
CircularB b;
public CircularB getB() {
return b;
}
public void setB(CircularB b) {
this.b = b;
}
}
class CircularB {
//在B中依赖A
CircularA a;
public CircularA getA() {
return a;
}
public void setA(CircularA a) {
this.a = a;
}
}
运行正常!
分享源码
Spring中解决循环依赖?
Spring是利用三级缓存来解决缓存依赖问题的。
一级缓存:存储的是 成品Bean对象,存储的所有单例对象,循环依赖没有关系
二级缓存:储存的是半成品对象,是解决循环依赖问题的关键,如果不去考虑AOP代理增加的情况,只有二级缓存也能解决循环依赖问题,也就是不需要三级缓存
三级缓存:这一级存在的原因就是解决AOP增强对象的原因,储存的是一个Lambda表达式(内部类)ObjectFactory
我的博客里有分析Spring解决循环依赖的问题的文章。