• 怎么解决Spring的循环依赖



    前言

    首先要知道Spring的依赖注入也就是DI,分为setter注入和构造器注入。但是Spring能够解决的是setter注入,构造器注入是不能够解决的。回到Spring的单例模式和多例模式下,多例模式下的依赖注入也是解决不掉的。所以只关注setter注入的循环依赖解决。其次,Spring的生命周期分为:实例化、属性注入、初始化和销毁。


    一、什么是循环依赖?

     @Component
        public class A{
            @Autowired
            B b;
        }
        @Component
        public class B{
            @Autowired
            A a;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    我们定义这样两个类一个是A,一个是B。但是A中有一个属性引用的B,B中的一个属性引用了A,这就是一个典型的循环依赖,形成一个圈,也可以是多个bean循环依赖,比如A依赖B,B依赖C,C依赖A。实例化A的时候,我们去属性赋值B,B也去实例化,B属性赋值A,然后A再去找B,于是产生了死循环。这就是循环依赖问题。

    二、三级缓存机制

        /** 一级缓存,用于储存完全初始化好的bean  **/
        private final Map<String,Object> singletonObject = new ConcurrentHashMap<String, Object>(256);
    
        /** 二级缓存,存放原始的bean对象(属性尚未填充),用于解决循环依赖 **/
        private final Map<String,Object> earlySingletonObject = new HashMap<String, Object>(16);
    
        /** 三级缓存,存放bean工厂对象,用于解决循环依赖问题 **/
        private  final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果只有一级缓存,那会怎样?
    首先A对象进行实例化,A要进行属性填充B。但B还没有创建,于是开始B进行实例化,同样B也要进行属性填充,发现它也需要A。然而发现一级缓存中还没有A,然后又创建A,于是就产生了死循环。循环往复,直到栈溢出。那么如果直接将A放入一次缓存,会产生什么后果呢?如果直接把A放入map中,map中的A就是个假A,缺胳膊少腿的,当真正使用这个A的时候,就会出现空指针异常。而且我们规定一级缓存只能存放完全初始化好的bean。

    我们知道一级缓存无法储存 半成品对象,也就是没有完全初始化好的对象,所以就引入了二级缓存,来储存这个东西。专门储存开始创建但是不完整的对象。我们把一级缓存叫map1,二级缓存叫map2。首先我们还是从实例化A开始,我们实例化后,还没有进行属性填充的时候,就把A对象放入map2备用。然后进行属性填充,A去填充B,发现B没有实例化,于是B同样实例化,把自己的半成品放入map2中。B开始进行填充,发现map1中没有A,再去map2中去寻找,发现map2中有。于是B直接从map2中拿到A使自己变得完整。这时候B就会把自己放入map1中,并把map2中的半成品删除了。回到A的阶段,A发现map1中有B了,那么A也完成了属性创建。于是双方都完成了自己的创建。这就是二级缓存解决的问题。

    那么这么来说,二级缓存就可以解决循环依赖问题,那问什么要搞出来一个三级缓存呢?
    主要是因为Spring的Aop机制所产生的代理对象问题。
    首先要了解Spring的代理对象产生阶段是在填充属性完成后才进行的,原理通过后置处理器BeanPostProcessor来实现。如果说我们如果用二级缓存来解决,那么就要在属性填充的时候就要将代理对象生成好,放入二级缓存。那这就与Spring的对象生命周期相反。所以就要引入三级缓存。
    首先我们还是先实例化A,这个时候就把A的ObjectFactory对象放入map3中。同样进行属性填充,这样B还没有创建,于是就去创建B。于是就去实例化B,那么在实例化B的过程中,我们也一样,先把B的ObjectFactory放入map3中。再把B对象放入map2中。继续执行B的属性填充,去获取A的对象,此时map1中没有A,因为A还没有创建,但是我们发现map3中有A的ObjectFactory对象,那么我们通过ObjectFactory对象获得A的早期对象,然后将这个早期对象放入map2中,同时删除了map3中的A,然后我们将A的引用给了B,那么这个B就是完整的了,就把B放入map1中同时删除map3中的B。这时候B已经创建完成了,A继续执行B的属性填充可以拿到B对象,这样A也可以完成创建,最后我们把A对象也放入map1,删除map2中的A,这样循环依赖就可以解决了。

  • 相关阅读:
    学习机器人无从下手?带你体会当下机器人热门研究方向有哪些
    GBase 8c V3.0.0数据类型——范围操作符
    【Oculus Interaction SDK】(十)在 VR 中使用手势识别
    Vue2 05 计算属性
    SDUT 2022 summer team contest 6th(for 21)
    element-ui——select多选框远端搜索组件封装
    大衍数列-蓝桥杯?-Lua 中文代码解题第2题
    php面试总结
    第十九章总结(绘制图片)
    【python中级】func_timeout程序超时处理
  • 原文地址:https://blog.csdn.net/m0_51529857/article/details/126200581