• Spring 是怎么处理循环依赖的?


    Java语法中的循环依赖

    首先看一个使用构造函数的循环依赖,如下:

    public class ObjectA {
        private ObjectB b;
        public ObjectA(ObjectB b) {
            this.b = b;
        }
    }
    
    public class ObjectB {
        private ObjectA a;
    
        public ObjectB(ObjectA a) {
            this.a = a;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            //ObjectB b = new ObjectB(new ObjectA(new ObjectB()));
        }
    }
    

    大家可以看上面这个例子,可以看出是没有办法new出ObjectA或者ObjectB的

    那怎么解决上面的例子呢?如下:

    public class ObjectA {
        private ObjectB b;
    
        public void setB(ObjectB b) {
            this.b = b;
        }
    
        public ObjectB getB() {
            return this.b;
        }
    }
    
    public class ObjectB {
        private ObjectA a;
    
        public void setA(ObjectA a) {
            this.a = a;
        }
    
        public ObjectA getA() {
            return this.a;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            ObjectB b = new ObjectB();
            ObjectA a = new ObjectA();
            b.setA(a);
            a.setB(b);
            System.out.println(a + " " + a.getB());
            System.out.println(b + " " + b.getA());
        }
    }
    

    输出如下:

    cn.eagle.li.spring.dependence.demo1.ObjectA@4534b60d cn.eagle.li.spring.dependence.demo1.ObjectB@3fa77460
    cn.eagle.li.spring.dependence.demo1.ObjectB@3fa77460 cn.eagle.li.spring.dependence.demo1.ObjectA@4534b60d
    

    可以看出把构造函数去掉,然后增加set方法就可以实现循环依赖的问题了。

    Spring 的构造函数循环依赖

    测试例子如下:

    @Component
    public class MyBeanOne {
        private MyBeanTwo myBeanTwo;
    
        @Autowired
        public MyBeanOne(MyBeanTwo myBeanTwo) {
            this.myBeanTwo = myBeanTwo;
        }
    }
    
    @Component
    public class MyBeanTwo {
        private MyBeanOne myBeanOne;
    
        @Autowired
        public MyBeanTwo(MyBeanOne myBeanOne) {
            this.myBeanOne = myBeanOne;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context =
                    new AnnotationConfigApplicationContext(MyBeanOne.class, MyBeanTwo.class);
            MyBeanOne myBeanOne = context.getBean(MyBeanOne.class);
            MyBeanTwo myBeanTwo = context.getBean(MyBeanTwo.class);
            System.out.println(myBeanOne);
            System.out.println(myBeanTwo);
        }
    }
    

    输出如下:

    Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'myBeanOne': Requested bean is currently in creation: Is there an unresolvable circular reference?
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355)
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:227)
    

    DefaultSingletonBeanRegistry.beforeSingletonCreation()里打断点,看一下为什么出错了:
    第一次经过:

    第二次经过:

    第三次经过:

    我们可以猜测出错的原因是这样的:

    去生成myBeanOne,需要生成myBeanTwo
    去生成myBeanTwo,需要生成myBeanOne
    去生成myBeanOne,发现myBeanOne已经在创建中了
    

    Spring 的Set方式循环依赖

    @Component
    public class MyBeanOne {
        @Autowired
        @Getter
        private MyBeanTwo myBeanTwo;
    }
    
    @Component
    public class MyBeanTwo {
        @Autowired
        @Getter
        private MyBeanOne myBeanOne;
    }
    
    public class Main {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context =
                    new AnnotationConfigApplicationContext(MyBeanOne.class, MyBeanTwo.class);
            MyBeanOne myBeanOne = context.getBean(MyBeanOne.class);
            MyBeanTwo myBeanTwo = context.getBean(MyBeanTwo.class);
            System.out.println(myBeanOne + " " + myBeanOne.getMyBeanTwo());
            System.out.println(myBeanTwo + " " + myBeanTwo.getMyBeanOne());
        }
    }
    

    输出:

    cn.eagle.li.spring.dependence.demo3.MyBeanOne@3c407114 cn.eagle.li.spring.dependence.demo3.MyBeanTwo@35ef1869
    cn.eagle.li.spring.dependence.demo3.MyBeanTwo@35ef1869 cn.eagle.li.spring.dependence.demo3.MyBeanOne@3c407114
    

    可以看出通过set注入的方式是可以解决循环依赖问题的。

    我们继续在DefaultSingletonBeanRegistry.beforeSingletonCreation()里打断点,看一下set方式是怎么经过这里的:
    发现只经过了两次,没有第三次,如果有第三次的话,也就抛异常了,所以只经过两次是正常的。

    为什么构造函数有三次,而set方式有两次?

    我们看一下DefaultSingletonBeanRegistry.beforeSingletonCreation的调用链:

    AbstractBeanFactory.doGetBean
    DefaultSingletonBeanRegistry.getSingleton
    DefaultSingletonBeanRegistry.beforeSingletonCreation
    

    那我们就在AbstractBeanFactory.doGetBean这里打断点,看一下set方式,为什么没有第三次
    第一次:

    第二次:

    第三次:

    我们可以看到第三次的myBeanOne已经有值了,它就不会执行到DefaultSingletonBeanRegistry.beforeSingletonCreation
    如果换成构造方式来调试的话,在第三次,myBeanOne依旧是null的,就会继续往下执行到DefaultSingletonBeanRegistry.beforeSingletonCreation,然后就会抛错。

    我们来看一下第三次的myBeanOne是怎么获取的:
    DefaultSingletonBeanRegistry.getSingleton 方法如下:

    可以看到是通过this.singletonFactories.get(beanName)得到一个工厂,通过这个工厂可以创建出对应的bean

    我们再来看一下这些个工厂是什么时候被放进去的,DefaultSingletonBeanRegistry.addSingletonFactory

    为什么通过构造函数注入的方式,没有提前放入一个工厂

    再执行AbstractAutowireCapableBeanFactory.createBeanInstance 方法时

    set方式会执行以下:

    调用这个方法之后,回退到 AbstractAutowireCapableBeanFactory.doCreateBean() 继续往下执行,会把工厂放进去。

    构造函数的方式会执行以下:

    调用这个方法,后面会继续获取myBeanTwo,如下:

    然后同理再获取myBeanOne,就会抛异常了,它不会回退到 AbstractAutowireCapableBeanFactory.doCreateBean(),自然也不会把工厂放进去。

    最后理一下

    对于Set方式,当类构造好之后,会提前把生成这个类的工厂放到缓存中;而构造函数的方式,由于存在构造函数,必须在当下去获取依赖类,所以就没办法构造类,其实原理和刚开始举的Java的例子是一个道理。

    参考

    Spring 解决循环依赖必须要三级缓存吗?
    Spring循环依赖三级缓存是否可以去掉第三级缓存?
    Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗

  • 相关阅读:
    【转】strlen和mb_strlen区别(php获得中英文混合字符长度)
    PHP字符串替换的相关方法介绍
    php表单转换textarea换行符的方法
    vue生命周期及其作用
    elemenui点击单行触发样式,选中或不选中复选框
    flutter 介绍和环境搭建
    flutter组件
    tora消息机制(事件监听,触发,取消)
    Promise功能与应用
    CCF CSP 20018031 跳一跳
  • 原文地址:https://www.cnblogs.com/eaglelihh/p/15862938.html
Copyright © 2020-2023  润新知