前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring中的循环依赖解决方案

Spring中的循环依赖解决方案

作者头像
naget
发布2019-07-03 15:48:16
2.1K0
发布2019-07-03 15:48:16
举报
文章被收录于专栏:VegoutVegout

Spring中的循环依赖

循环依赖:循环依赖就是循环引用,就是两个或多个bean互相之间持有对方。比如beanA引用beanB,beanB引用beanA,当我们实例化beanA的时候发现beanB作为beanA的成员对象出现了,那么我们就可能在实例化beanA的中间需要先实例化beanB,然后完成beanB的实例化之后,才能完成beanA的实例化,可惜的是beanB中也引用了beanA,在实例化beanB过程中又需要实例化beanA,而beanA正在进行实例化,但完成beanA的实例化的条件是beanB实例化完成,完成beanB实例化的条件是完成beanA的实例化,于是他们最终反映为一个环状依赖,难以完成实例化。

Spring如何解决循环依赖

spring中循环依赖有三种情况: 1、构造器注入形成的循环依赖。也就是beanB需要在beanA的构造函数中完成初始化,beanA也需要在beanB的构造函数中完成舒适化,这种情况的结果就是两个bean都不能完成初始化,循环依赖难以解决。 2、setter注入构成的循环依赖。beanA需要在beanB的setter方法中完成初始化,beanB也需要在beanA的setter方法中完成初始化,spring设计的机制主要就是解决这种循环依赖,也是今天下文讨论的重点。 3、prototype作用域bean的循环依赖。这种循环依赖同样无法解决,因为spring不会缓存‘prototype’作用域的bean,而spring中循环依赖的解决正是通过缓存来实现的。

下面主要说明第二种情况中循环依赖的解决方案

步骤一:beanA进行初始化,并且将自己进行初始化的状态记录下来,并提前向外暴露一个单例工程方法,从而使其他bean能引用到该bean(可能读完这一句,您仍然心存疑惑,没关系,继续往下读) 步骤二:beanA中有beanB的依赖,于是开始初始化beanB。 步骤三:初始化beanB的过程中又发现beanB依赖了beanA,于是又进行beanA的初始化,这时发现beanA已经在进行初始化了,程序发现了存在的循环依赖,然后通过步骤一中暴露的单例工程方法拿到beanA的引用(注意,此时的beanA只是完成了构造函数的注入但为完成其他步骤),从而beanB拿到beanA的引用,完成注入,完成了初始化,如此beanB的引用也就可以被beanA拿到,从而beanA也就完成了初始化。

spring进行bean的加载的时候,首先进行bean的初始化(调用构造函数),然后进行属性填充。在这两步中间,spring对bean进行了一次状态的记录,也就是说spring会把指向只完成了构造函数初始化的bean的引用通过一个变量记录下来,明白这一点对之后的源码理解至关重要。

源码角度观看循环依赖的解决步骤

步骤一中首先进行beanA的创建

                    if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });

进入getSingleton中,spirng会记录当前beanA正在创建中

        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

并且将注册一个工厂方法来解决循环依赖

        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
     这里:    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

主要就是addSingletonFactory,这句就完成了工厂方法的注册,这个方法可以返回一个只完成了构造函数初始化的beanA,也许大家想知道他是如何返回的,我们进入getEarlyBeanReference方法

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

可以看到这个方法中,除了对后处理器的调用,没有进行任何动作,而是直接返回了我们参数传入的bean,那么这个bean是哪来的呢?其实在这之前,spring先调用了beanA的构造函数,并拿到了只完成了构造函数初始化的一个实例,并把他记录了下来。

if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();

这就是这个bean的由来。然后当我们进行到步骤三的时候,就会检查是否允许循环依赖(即使是Singleton类型的bean也可以通过参数设置,禁止循环依赖),如果允许的话,就会通过这个工厂方法拿到beanA的引用。从而完成beanA和beanB的加载。

    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;
    }

一个不甚恰当的循环依赖类比

看比尔盖茨的女婿和世界银行部总裁这两个身份如何解决循环依赖

视频内容
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-07-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Vegout 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring如何解决循环依赖
  • 下面主要说明第二种情况中循环依赖的解决方案
  • 源码角度观看循环依赖的解决步骤
  • 一个不甚恰当的循环依赖类比
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档