Spring 解决循环依赖

1.什么是循环依赖?                                                                                 

顾名思义:循环+依赖 ;

        - 类与类之间的依赖关系形成了闭环。

 常见的循环依赖有以下三种情况:

 - 自身依赖于自身

 - 互相循环依赖

 - 多组循环依赖

2.代码演示依赖

public class YanshiTest {

    public static void main(String[] args) {
        new Test1();
    }
}
class Test1{
    private Test2 test2 = new Test2();
}

class Test2{
    private Test1 test1 = new Test1();
}

 Test1 依赖 Test2     Test2 依赖 Test1

我们执行代码后结果:

Exception in thread "main" java.lang.StackOverflowError
	at com.ape.test.Test2.<init>(YanshiTest.java:21)
	at com.ape.test.Test1.<init>(YanshiTest.java:17)
	at com.ape.test.Test2.<init>(YanshiTest.java:21)
	at com.ape.test.Test1.<init>(YanshiTest.java:17)
	at com.ape.test.Test2.<init>(YanshiTest.java:21)
	at com.ape.test.Test1.<init>(YanshiTest.java:17)
	at com.ape.test.Test2.<init>(YanshiTest.java:21)
	at com.ape.test.Test1.<init>(YanshiTest.java:17)
	at com.ape.test.Test2.<init>(YanshiTest.java:21)
	at com.ape.test.Test1.<init>(YanshiTest.java:17)
	at com.ape.test.Test2.<init>(YanshiTest.java:21)
	at com.ape.test.Test1.<init>(YanshiTest.java:17)

这样的循环循环了很多行才停,出现了StackOverflowError 的错误,这就是循环依赖。

3.如何解决

首先我们进行原因分析:

        -   Test1在构造方法中需要获取Test2的对象,然后在创建Test2中有需要Test1的对象。

解决问题的关键是我们要把对象的创建拆分为:

        -  构造方法

        -  成员变量赋值 ==》get/set 方法处理

首先先给Test1 与Test2 写GET/SET方法

public class YanshiTest {
    public static void main(String[] args) {
        System.out.println( new Test1().getTest2());
    }
}
    class Test1{
        private Test2 test2;
        public Test2 getTest2() {
            return test2;
        }
        public void setTest2(Test2 test2) {
            this.test2 = test2;
        }
    }
    class Test2{
        private Test1 test1 ;

        public Test1 getTest1() {
            return test1;
        }

        public void setTest1(Test1 test1) {
            this.test1 = test1;
        }
    }

-  需要把构造方法与属性赋值 作为一个整体 

-  需要提供一个获取实例的方法

        这个方法需要 根据类型获取一个对应的实例对象 因此定义为泛型

        这个方法需要 完成构造  完成成员变量的赋值

 public static <T> T getBean(Class<T> classname) throws Exception{
        //1.通过构造方法获取实例对象
        Object t = classname.newInstance();
        //2.给成员变量赋值
        Field[] fields = classname.getDeclaredFields();
        //遍历
        for (Field field : fields) {
            //赋值
            field.setAccessible(true);
            Class<?> type = field.getType();
            field.set(t,getBean(type));
        }
        return (T)t;
    }

此时我们在main方法进行调用还是会报错,因此我们需要拆解来看

首先 我们想如果把获取的实例对象存储到Map容器 再去获取需要的对象时,就不需要去new了,直接从map里面取出赋值给Test02,这样就不会出现循环依赖了,但是这个map是个半成品,因此我们需要做一些操作:

 //存储半成品的容器——解决循环依赖
    private static final Map<String,Object> map =new ConcurrentHashMap<String, Object>();

此时只需两步

        1.判断map是否有对象

        2.没有就放进去

 public static <T> T getBean(Class<T> classname) throws Exception{
        //--获取类型的名称
        String beanName = classname.getSimpleName().toLowerCase();
        //--如果map中有对象直接返回
        if(map.containsKey(beanName)){
            return (T)map.get(beanName);
        }
        //1.通过构造方法获取实例对象
        Object t = classname.newInstance();
        //--创建的这个半成品对象我们需要存储在map容器中
        map.put(beanName,t);
        //2.给成员变量赋值
        Field[] fields = classname.getDeclaredFields();
        //遍历
        for (Field field : fields) {
            //赋值
            field.setAccessible(true);
            Class<?> type = field.getType();
            field.set(t,getBean(type));
        }
        return (T)t;
    }

在进行运行:

 

解决掉了,完美撒花。  解决这个的关键点在于提前暴露半成品对象。

上面的方法中的核心是getBean方法,Test1 创建后填充属性时依赖Test2,那么就去创建 Test2,在创建 Test2 开始填充时发现依赖于 Test1,但此时 Test1 这个半成品对象已经有了(其实是个缓存),所以Test2可以正常创建,在通过递归把 Test1 也创建完整了。

3.循环依赖

基于前面案例的了解,我们知道肯定需要在调用构造方法方法创建完成后再暴露对象,在Spring中提供了三级缓存来处理这个事情:

首先在调用构造方法的后会放入到三级缓存中--》然后在填充属性的时候会存入二级缓存中--》最后把创建的对象保存在了一级缓存中

就是这样啦!下个星期见!

  • 11
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值