美文网首页
spring容器之bean创建循环依赖处理

spring容器之bean创建循环依赖处理

作者: 会上树的程序猿 | 来源:发表于2019-07-22 23:30 被阅读0次

    还是回到我们创建bean的#doCreateBean(...)方法中,上节我们简单的了解了对于该方法创建完成的bean总是会对其进行属性的填充,这篇我们来了解下该方法中第3个过程,对于循环依赖的处理过程,首先我们得了解下什么是循环依赖

    循环依赖

    所谓循环依赖就是两个或者两个以上的引用相互引用,最终会形成一个闭环,比如:A依赖B,B依赖C,C依赖A如图所示:

    image

    图中更能直接反应循环依赖的过程,实质是一个死循环的过程,是没法终止的过程,除非强制性终止,在图中当spring初始化A实例时,发现需要引用B实例,所以先初始化实例B,发现需要先初始化实例C,大致就是这样

    spring中循环依赖的场景
    • 单例场景下的循环依赖
    • 原型(prototype)场景下的循环依赖

    不知道大家记得在我们的spring容器之开启bean的创建之旅这篇文章中,关于bean的创建过程中,我们说过,spring对于依赖处理只针对于单例的情况下,在原型的情况下spring会统一抛出BeanCurrentlyInCreationException异常,所以在接下来的学习中,我们来看spring对于单例情况下的循环依赖处理:

    获取单例实例

    我们先从原始的创建bean的入口开始,也就是#doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)该方法,该方法位于AbstractBeanFactory.java类中.

    在doGetBean(...)方法中有这样一段代码值得注意:

    AbstractBeanFactory.java
    Object sharedInstance = getSingleton(beanName);
    

    简单的一行代码,其主要的作用是通过指定的beanName去获取单例bean,在缓存中存在的情况下,直接返回即可,接着看:

    DefaultSingletonBeanRegistry.java
    /**
     *
     * @param beanName 转化之后的bean的beanName
     * @param allowEarlyReference  允许早期依赖
     * @return
     */
    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //通过beanName从缓存中去拿对应的实例
        Object singletonObject = this.singletonObjects.get(beanName);
        //如果为null且当前的单例的bean正在创建过程中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            //锁定该全局变量singletonObjects进行相关的处理
            synchronized (this.singletonObjects) {
                //从早期单例bean缓存中去获取
                singletonObject = this.earlySingletonObjects.get(beanName);
                //如果为null,且提前创建
                if (singletonObject == null && allowEarlyReference) {
                    //从单例工厂中获取
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        //调用getObject并获取对应的实例
                        singletonObject = singletonFactory.getObject();
                        //保存到earlySingletonObjects缓存中
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        //同时移除singletonFactories中的对应的objectFactory
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }
    

    该方法主要是从三种缓存中分别获取,关于三个缓存的讲解我们在之前的文章已经详细的说过了,不清楚的可以去看看.

    • singletonObjects是单例对象的缓存
    • singletonFactories是单例对象工厂的缓存
    • earlySingletonObjects主要的作用是提前曝光单例对象的缓存

    在上述代码中我们看到有两个判断的条件分别是:

    • isSingletonCurrentlyInCreation(beanName)
    • allowEarlyReference变量

    关于isSingletonCurrentlyInCreation(beanName):其主要的作用是判断当前bean是否在创建的过程中,如果返回为true则表示我们的bean正在初始化且没完成初始化,在spring中是有专门的属性来记录此刻bean的状态,且不同scope的bean有不同的状态,这也是spring在解决单例bean的过程中为什么会选择提前曝光创建的bean

    allowEarlyReference:从字面意思上面理解就是允许提前拿到引用.其实真正的意思是,是否允许从 singletonFactories 缓存中通过 #getObject() 方法,拿到对象。为什么会有这样一个字段呢?原因就在于 singletonFactories 才是 Spring 解决 singleton bean 的诀窍所在(借鉴别人的,咋也不是很清楚)

    知道了上面这两个变量用途之后,我们得想一个问题,既然我们是从这三个缓存中获取单例bean的,那么缓存的中的bean是从何而来,是什么时候加载进去的呢?这是我们需要的考虑的地方,其实不然在我们的#doCreateBean(...)方法中有这样一段代码:

    AbstractAutowireCapableBeanFactory.java
    //4.对单例模式下的循环依赖进行检查
        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");
            }
            //避免后期循环依赖,提早曝光创建的bean并加入到addSingletonFactory中
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
    

    说实话,代码不难,但是却耐人寻味,这段代码究竟干了什么其实我也不是很懂,只知道当一个bean满足上面所给的这三个条件时:

    • 是一个单例bean
    • 允许引用提前曝光的bean
    • 当前bean正在初始化的过程中

    最后通过方法#addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)方法完成依赖bean的添加,何时加入的.就是在这里,我们来看看该方法的实现过程:

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

    可以看到,这里就是spring提早曝光创建的bean,可能此时的bean不是成熟的bean,也不是我们需要的bean,之所以spring这样搞,是让我们对这些bean在完成创建之前有一个认识的过程,到这里基本上就大概的了解了循环依赖何时处理何时暴露这些bean的....

    小结

    这样我们还是简单的以AB循环依赖为case进行分析,我们的类A中拥有属性B,且类B中包含属性A,在初始化Abean的过程中如图所示:

    image
    • 从图中可以看到,在创建A时,记录A的beanName属性,之后暴露A.
    • 在对A进行属性填充的过程中,发现A依赖B,此时先完成B的初始化过程,最后加入到addSingletonFactory(...)并暴露自己.
    • 当在一次属性填充时,由于B中依赖于A,那么在一次的会初始化B,之后调用getBean(A)是从缓存中去检测是否已经有创建好的Abean,或者是ObjectFactory类型的bean,(这里我们在初始化类A时,它的ObjectFactory已经完成了创建),接着调用ObjectFactory的getBean()去创建A.

    这就是上图中AB循环依赖的过程,关于本篇的知识点也就到这里了....

    相关文章

      网友评论

          本文标题:spring容器之bean创建循环依赖处理

          本文链接:https://www.haomeiwen.com/subject/jzjglctx.html