深入解析Spring容器解决循环依赖的原理

最近看源码在研究类似如下配置的循环依赖是怎么解决的?

<bean id="a" class="com.project.demo.A" scope="singleton">
      <property name="b" ref="b"/>
</bean>
<bean id="b" class="com.project.demo.B" scope="singleton">
      <property name="a" ref="a"/>
</bean>

说明:

1、Spring容器解决循环依赖的问题配置类必须是单例模式scope="singleton"才支持,如果是scope="prototype"是无法解决循环依赖的。

2、Spring容器解决循环依赖主要依靠三级缓存机制

2.1 一级缓存使用的map: private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

2.2 二级缓存使用的map: private final Map<String, Object> earlySingletonObjects = new HashMap(16);

2.3 三级缓存使用的map: private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);

3、Spring容器解决循环依赖简洁概述主要有四大流程方法:获取对象 getSingleton()、 创建对象(实例化) doCreateBean()、填充属性(初始化) populateBean()、返回对象 addSingleton()

在系统启动获取配置文件后,程序是依次读取并加载的,所以上面配置文件代码,先实例化a对象,然后初始化a对象给a添加b属性,再实例化b对象,最后初始化b对象给添加属性a.

那么在代码执行过程中,先调用getSingleton()方法,我们查看源码

@Nullable
    public Object getSingleton(String beanName) {           // 调用下方重载方法
        return this.getSingleton(beanName, true);
    }

    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {       // 先从一级缓存中获取a对象的实例
        Object singletonObject = this.singletonObjects.get(beanName);           // 如果从一级缓存中获取不到a对象,那么检查该对象是否正在被创建,如果正在被创建,则进入if循环中
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            synchronized(this.singletonObjects) {            // 从二级缓存中获取该对象
                singletonObject = this.earlySingletonObjects.get(beanName);                   // 如果二级缓存中无法获取该对象,那么一定会进入如下if方法,因为allowEarlyReference传过来的时候就是true
                if (singletonObject == null && allowEarlyReference) {              // 从三级缓存中获取该对象
                    ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {                           // 如果获取到了该对象,就将三级缓存中的对象放到二级缓存中,并且将三级缓存中的对象删除
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }

        return singletonObject;
    }

从三面的源码发现,如果a第一次获取,那么第9行的if语句为false,将直接放回为null,这时回到创建对象doCreateBean()方法,该方法使用反射的方式生成a对象,并且该对象在三级缓存中,对象生成后就需要对a对象进行属性填充:

 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
         // 省略多行代码,大致就是调用各种方法,通过反射创建对象
         try {
             // a对象创建完成,调用属性填充方法,对a进行属性填充
             this.populateBean(beanName, mbd, instanceWrapper);
             exposedObject = this.initializeBean(beanName, exposedObject, mbd);
         } catch (Throwable var18) {
             if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
                 throw (BeanCreationException)var18;
             }
 
             throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
         }
 
         if (earlySingletonExposure) {
             Object earlySingletonReference = this.getSingleton(beanName, false);
             if (earlySingletonReference != null) {
                 // 省略多行代码
             }
         }
         // 省略多行代码
     }

在上面代码doCreateBean()方法中先创建a对象,创建完成后会调用this.populateBean(beanName, mbd, instanceWrapper)方法对a进行属性填出,这个时候会获取配置文件中所有<bean id="a">里面的所有属性,发现会存在一个b属性,下面贴出部分源码

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        if (bw == null) {
            if (mbd.hasPropertyValues()) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
        } else {
            boolean continueWithPropertyPopulation = true;
            if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
                // 删除大量代码
            }

            if (continueWithPropertyPopulation) {
                // 删除大量源代码,applyPropertyValues方法中beanName为a,pvs为状态各种属性的PropertyValues对象,pvs就装有b这个属性
                if (pvs != null) {
                    this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs);
                }

            }
        }
    }

继续跟进applyPropertyValues方法的源码

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        if (!pvs.isEmpty()) {
            if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
                ((BeanWrapperImpl)bw).setSecurityContext(this.getAccessControlContext());
            }
            MutablePropertyValues mpvs = null;
            List original;
            if (pvs instanceof MutablePropertyValues) {
                // 省略大量代码
            } else {
                original = Arrays.asList(pvs.getPropertyValues());
            }
            // 省略大量代码 大致过程是将属性对象pvs 转化成original List对象,然后在使用迭代器在下面进行迭代
            Iterator var11 = original.iterator();
            while(true) {
                while(var11.hasNext()) {
                    PropertyValue pv = (PropertyValue)var11.next();
                    if (pv.isConverted()) {
                        deepCopy.add(pv);
                    } else {
                        String propertyName = pv.getName();
                        Object originalValue = pv.getValue();
                        // 通过下面方法解决依赖的b,整个方法在迭代器中,外层在while(true)中,可能有多个属性,循环直到所有属性都解决了就return;或者抛出异常
                        Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
                        // 省略大量代码
                    }
                }

                if (mpvs != null && !resolveNecessary) {
                    mpvs.setConverted();
                }

                try {
                    bw.setPropertyValues(new MutablePropertyValues(deepCopy));
                    return;
                } catch (BeansException var19) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", var19);
                }
            }
        }
    }

继续跟进上面红色方法

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
        if (value instanceof RuntimeBeanReference) {
            RuntimeBeanReference ref = (RuntimeBeanReference)value;
            return this.resolveReference(argName, ref);
        } else if (value instanceof RuntimeBeanNameReference) {
            // 省略多行代码
        }
        // 省略多行代码
    }

继续跟进红色部分的代码

private Object resolveReference(Object argName, RuntimeBeanReference ref) {
        try {
            String refName = ref.getBeanName();
            refName = String.valueOf(this.doEvaluate(refName));
            Object bean;
            if (ref.isToParent()) {
                // 省略多行代码
            } else {
                // 通过refName的值b又去工厂找b对象
                bean = this.beanFactory.getBean(refName);
                this.beanFactory.registerDependentBean(refName, this.beanName);
            }

            // 省略多行代码
            return bean;
        } catch (BeansException var5) {
            throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, var5);
        }
    }
public Object getBean(String name) throws BeansException {
         return this.doGetBean(name, (Class)null, (Object[])null, false);
     }
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
        String beanName = this.transformedBeanName(name);
        // 跟了这么就,最终表明在实例化a对象后,在装载a的属性b时,会经过各种校验最终到getSingleton(),及先获取b对象,如果b对象不存在则会对b完成创建的过程
        Object sharedInstance = this.getSingleton(beanName);
        // 省略多行代码
    }

上面整个过程概括:在检测到需要实例化a时,先去获取a对象,看a是否已经存在,获取去先从一级缓存中获取,如果没有并且如果a也没有正在实例化,那么直接返回null,表明获取不到a对象,那么此时调用doCreateBean()方法完成对a对象的实例化过程(通过反射创建a对象),并且将创建的a对象放在三级缓存中,然后继续执行doCreateBean中的populateBean()方法完成对a进行初始化即添加属性b,经过一些列校验,最终又会调用getSingleton()方法来获取b对象,同样会返回null,这个时候就会去执行doCreateBean()方法创建b对象,同样过反射创建b,当b对象创建完成时也会存放在三级缓存中,在实例化b对象完成,然后继续执行doCreateBean中的populateBean()方法,也需要初始化b对象,填充b的属性,这时发现b对象的属性是a,同样再次通过getSingleton()方法获取a,获取a的过程如第一个源码部分,先从一级缓存中获取,获取不到,然后判断a正在创建中,然后就从二级、三级缓存中获取,最终在三级缓存中获取到了a,并且将三级缓存中的a对象放到二级缓存中,并将删除三级缓存中的a,此时b对象初始化也完成。

在a对象初始化的流程中,将b对象也实例化和初始化了,在b的初始化过程中,将a从三级缓存移到了二级缓存中,当b初始化完成后继续向下执行,会执行到addSingleton(),查看源码

protected void addSingleton(String beanName, Object singletonObject) {
        synchronized(this.singletonObjects) {       // 将b加入到1级缓存
            this.singletonObjects.put(beanName, singletonObject);            // 将b从三级缓存中删除
            this.singletonFactories.remove(beanName);            // 将b从二级缓存中删除(b在二级缓存中没有,即空删除)
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

从上面代码可以看出在,并创建完成并且初始化后,会将B从三级缓存中直接放到一级缓存中,并且删除三级缓存中的数据。

所有b工作做完后返回到a初始化属性b的代码

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {

        // 省略大量代码
        try {
            // 完成a对于属性b的检查装配工作后返回到方法populateBean(),继续向下执行
            this.populateBean(beanName, mbd, instanceWrapper);
            exposedObject = this.initializeBean(beanName, exposedObject, mbd);
        } catch (Throwable var18) {
            if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
                throw (BeanCreationException)var18;
            }

            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
        }

        if (earlySingletonExposure) {
            // a执行到该方法getSingleton(),这里面会将a从二级缓存中取出来
            Object earlySingletonReference = this.getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                // 省略大量代码
            }
        }
        // 省略大量代码
    }

继续执行,最终也会到addSingleton()方法,将a也加入一级缓存,并且从二级缓存中删除a。

这样a和b两个循环依赖的bean都被放入到一级缓存中。


最新2020整理收集的一些高频面试题(都整理成文档),有很多干货,包含mysql,netty,spring,线程,spring cloud、jvm、源码、算法等详细讲解,也有详细的学习规划图,面试题整理等,
需要获取这些内容的朋友请加Q君样:
11604713672

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容