zoukankan      html  css  js  c++  java
  • Spring5源码阅读--如何解决循环依赖总结

    首先要明确的是,循环依赖有以下几种:
    ①:构造函数的循环依赖。这种依赖显然是解决不了的。
    ②:非单例Bean的循环依赖。这种依赖也是解决不了的。
    ③:单例Bean的循环依赖。本文介绍的就是如何解决单例Bean的循环依赖的问题。

    第一次处理循环依赖的地方是doGetBean()方法中的: 

    Object sharedInstance = getSingleton(beanName);
        protected Object getSingleton(String beanName, boolean allowEarlyReference) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
                synchronized (this.singletonObjects) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null && allowEarlyReference) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
            return singletonObject;
        }

    这个方法其实之前分析过的,我们可以这样理解循环依赖有三层的缓存。
    singletonObjects:一级缓存,存放的是beanName和bean实例。
    earlySingletonObjects:二级缓存,存放的是beanName和已经被初始化但是没有创建完成的bean实例。也就是允许提前曝光的bean的实例
    singletonFactories:三级缓存,存放的是beanName和ObjectFactory工厂。
    通过这三层缓存的有效的解决了Spring的属性循环依赖问题。这个方法还有两个方法要解析。
    isSingletonCurrentlyInCreation方法:判断bean是否在创建中。
    allowEarlyReference:判断bean是否可以被提前曝光。
    getSingletion()这个逻辑:从一级缓存singletonObjects中获取,如果没有当前beanName的是实例并且正在创建中,那么就从二级缓存中earlySingletonObjects获取,如果还是没有获取到,那么从三级缓存中获取并调用getObject()去获取bean的实例,并将其加入二级缓存earlySingletonObjects,从三级缓存singletonFactory中删除。
    那么三级缓存的添加是从哪里开始的。追踪createBean()方法,在doCreateBean()方法中

            // Eagerly cache singletons to be able to resolve circular references
            // even when triggered by lifecycle interfaces like BeanFactoryAware.
            boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                    isSingletonCurrentlyInCreation(beanName));
            if (earlySingletonExposure) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Eagerly caching bean '" + beanName +
                            "' to allow for resolving potential circular references");
                }
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            }

    当earlySingletonExposure==true时,会去执行addSingletonFactory()方法。
    那满足添加三级缓存的的条件是:
    ①:mbd.isSingleton():单例模式
    ②:this.allowCircularReferences:该bean允许被提前曝光
    ③:isSingletonCurrentlyInCreation(beanName):正在处于创建中

        protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(singletonFactory, "Singleton factory must not be null");
            synchronized (this.singletonObjects) {
                if (!this.singletonObjects.containsKey(beanName)) {
                    this.singletonFactories.put(beanName, singletonFactory);
                    this.earlySingletonObjects.remove(beanName);
                    this.registeredSingletons.add(beanName);
                }
            }
        }

    这个段代码是在createBeanInstance()之后,bean的实例是已经创建出来,但是并没有属性进行初始化。但是并不妨碍其他bean
    对这个bean的对象引用。
    下面分析一级缓存的添加逻辑在在类 DefaultSingletonBeanRegistry 中可以发现这个 addSingleton() 方法:
    addSingleton(beanName, singletonObject)方法在this.singletonObjects.get(beanName)之后

        protected void addSingleton(String beanName, Object singletonObject) {
            synchronized (this.singletonObjects) {
                this.singletonObjects.put(beanName, singletonObject);
                this.singletonFactories.remove(beanName);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }

    在Spring解决循环依赖的方案中:Spring在创建bean的时候并不是等完全完成,而是在创建过程中将创建的bean的ObjectFactory提前曝光(加入三级缓存),这样另外的bean需要依赖的bean,可以直接使用ObjectFactory的getObejct获取。

    到这里,关于 Spring 解决 bean 循环依赖就已经分析完毕了。最后来描述下就上面那个循环依赖 Spring 解决的过程:首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来,然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来,这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A),这个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存 singletonFactories ),通过 ObjectFactory 提前曝光,所以可以通过 ObjectFactory.getObject() 拿到 A 对象,C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中,回到 B ,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路就已经完成了初始化过程了。

  • 相关阅读:
    前端js实现复制功能
    vue中流文件下载
    vuex的module简单使用
    vuex及其辅助函数简单使用
    element中合并单元格操作
    js保留小数并四舍五入方法封装
    数组的基本属性和方法
    数组的深复制、扁平化、排序及去重相关方法
    js基本数据类型及数组对象判断
    for/in 语句用于循环对象属性
  • 原文地址:https://www.cnblogs.com/jelly12345/p/13026301.html
Copyright © 2011-2022 走看看