什么是循环依赖?

有以下三种依赖情况

循环依赖 JAVA 解决 java如何解决循环依赖_java

虽然方式不一样但是,依赖的本质是一样的,就是你完整的创建需要依赖我,我的完整也要依赖你,最终谁都都无法构建,造成构建失败

产生循环依赖的情况

/*
 *原因在创建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();
}

这样就产生了,栈溢出

循环依赖 JAVA 解决 java如何解决循环依赖_java_02

解决循环依赖

上面的主要问题就是,在B创建A的时候,拿不到A的实例,解决了这步,就是就解决了这个问题,也就是将创建过程分开,1、构造方法实例  2、成员变量赋值两个阶段

如下图

循环依赖 JAVA 解决 java如何解决循环依赖_spring_03

 开始改代码

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;
    }
}

在这中情况下,肯定没问题

循环依赖 JAVA 解决 java如何解决循环依赖_面试_04

但是此时还没有赋值,下面进行赋值

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;
    }
}

循环依赖 JAVA 解决 java如何解决循环依赖_赋值_05

但是此时仍然出现循环依赖问题,我们虽然是分成了两部,但是  field.set(obj,getBean(fieldType))这步仍然是循环依赖。

循环依赖 JAVA 解决 java如何解决循环依赖_spring_06

 这时我们提前暴露对象即可,我可以利用,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;
    }
}

运行正常!

循环依赖 JAVA 解决 java如何解决循环依赖_java_07

 

Spring中解决循环依赖?

Spring是利用三级缓存来解决缓存依赖问题的。

一级缓存:存储的是 成品Bean对象,存储的所有单例对象,循环依赖没有关系

二级缓存:储存的是半成品对象,是解决循环依赖问题的关键,如果不去考虑AOP代理增加的情况,只有二级缓存也能解决循环依赖问题,也就是不需要三级缓存

三级缓存:这一级存在的原因就是解决AOP增强对象的原因,储存的是一个Lambda表达式(内部类)ObjectFactory

我的博客里有分析Spring解决循环依赖的问题的文章。