• Spring循环依赖


    什么是循环依赖

    在Spring中,两个或多个Bean互相持有对方,最终反映为一个环,就构成了循环依赖。如:

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

    循环依赖产生的场景

    构造器循环依赖(不可解决)

    setter循环依赖(可以解决)

    prototype范围的循环依赖(不可解决)

    Spring如何解决循环依赖?

    三级缓存

    DefaultSingletonBeanRegistry中,Spring定义了三个map,这就是我们平时说的三级缓存

    // 一级缓存:用于存放完全初始化完成的bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    // 三级缓存:存放bean工厂对象
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    // 二级缓存:用于存放原始的bean对象(尚未填充属性)
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    getSingleton方法中对于三级缓存的使用

    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 尝试在singletonObjects中获取bean
        Object singletonObject = this.singletonObjects.get(beanName);
        // 如果没有获取到,并且该bean在创建中,
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            // 尝试在earlySingletonObjects获取bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 还是没有获取到,并且允许创建早期对象的引用
            if (singletonObject == null && allowEarlyReference) {
                // 因为需要操作singletonObjects,所以将其锁定
                synchronized (this.singletonObjects) {
                    // 再次尝试获取
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            // 在singletonFactories中获取其bean工厂
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                // 如果获取到bean工厂,通过工厂的getObject()方法获取其早期实例
                                singletonObject = singletonFactory.getObject();
                                // 之后将其早期实例存放到earlySingletonObjects中,也就是将三级缓存提高到了二级
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        // 返回实例
        return singletonObject;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    isSingletonCurrentlyInCreation()方法用于判断当前单例bean是否正在创建中

    img

    Spring为什么不能解决构造器循环依赖

    IOC大致流程:

    1.getBean -> 2.第一个getSingleton(在三级缓存取bean) -> 3.第二个getSingleton(去创造bean) -> 4.beforeSingletonCreation(向singletonsCurrentlyInCreation中添加正在创建的bean) -> 5.createBean -> 6.doCreateBean(从上至下调用的方法为:createBeanInstance,addSingletonFactory放入三级缓存中,populateBean填充属性,initializeBean初始化bean) -> 7.放入单例池中

    如果是构造器注入的话(假如有A、B类,A先B后),A第一次先把自己放入singletonsCurrentlyInCreation中,然后在createBeanInstance时会去调用@AutoWired标注的有参构造器(此时A没有实例化,连对象都没创建),然后会去getBean(B),这就回到了上方流程的开头,B在第一个getSingleton没有获取到A,然后就去getBean(A),对于A来说已经是第二次了,于是在向singletonsCurrentlyInCreation添加的时候就会报错,因为该集合已经有了A,因此异常在此处抛出。

    img

    Spring为什么不能解决prototype范围的循环依赖

    对于prototype范围的bean,Spring每次都会创建一个新的实例,该实例不会被缓存,因此不能提前暴露一个创建中的bean到缓存中,也就无法解决

    使用二级缓存行不行

    使用三级缓存主要针对的不是IOC,而是AOP。如果 Spring 选择二级缓存来解决循环依赖的话,那么就意味着所有 Bean 都需要在实例化完成之后就立马为其创建代理,而 Spring 的设计原则是在 Bean 初始化完成之后才为其创建代理。

    即:若使用二级缓存,在 AOP 情形注入到其他 Bean的,不是最终的代理对象,而是原始对象。

  • 相关阅读:
    PostgreSQL执行计划:Bitmap scan VS index only scan
    用递归函数遍历判断同一层级某个属性值是否相同
    什么是IA智能自动化?与RPA+AI有何区别?
    C# 常见形状
    某马旅游网站开发(对servlet的优化)
    计算机毕业设计ssm高校学报管理系统lt10k系统+程序+源码+lw+远程部署
    iceberg-flink 七:累积窗口使用。(CUMULATE)
    如何利用ChatGPT提升学术论文写作效率
    SpringBoot旅游网源码和论文java旅游管理系统
    时序预测 | MATLAB实现基于SVM-Adaboost支持向量机结合AdaBoost时间序列预测
  • 原文地址:https://blog.csdn.net/qq_43460095/article/details/125913992