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

还是回到我们创建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 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创建循环依赖处理)