当两个bean互相依赖,或者多个bean相互依赖并形成一个环状结构的时候,就形成了循环依赖的问题。例如下图:

spring el 关联bean_spring

关于bean的实例化过程,我在spring源码学习_bean的实例化过程中有详细讲解,这里直接说结论。

① bean在实例化时,spring首先会找到其指定的构造方法,生成这个bean的java对象。此时bean只是单纯的java对象,其中的依赖并没有被注入。这个阶段的bean我们可以理解为是半成品的bena或是早产的bean。

② 对象生成后,spring就会在spring容器中获取其依赖的对象,并将依赖对象赋值给属性。如果该依赖在spring容器中不存在,则先实例化这个依赖。

我们以上图的第一个情况为例。spring先实例化A,生成了A类的对象,在第二步注入属性时,发现spring容器中并没有B,则转去实例化B。B的对象创建后,又会尝试在Spring容器中寻找A,结果当然是找不到的。因为A现在还是半成品,并不是真正的bean,也没有被放入spring容器中。所以这时spring又得去实例化A。这样就形成了一个死循环,导致这两个bean永远都没法实例化。

尽管我们在实际开发中会避免设计出如此糟糕的依赖关系,但是spring依然为帮我们解决了这个问题。下面,我们就通过源码看看spring是如何解决的。

这里只贴出doGetBean方法中与循环依赖相关的代码

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

    Object bean;

    //这里尝试直接从缓存中获取bean
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else{
        //缓存中没有,则创建这个bean
        if (mbd.isSingleton()) {
            //这里通过lambda表达式,传入了一个对象工厂
            sharedInstance = getSingleton(beanName, () -> {
                try {
                    return createBean(beanName, mbd, args);
                }
                catch (BeansException ex) {....}
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }
    }
    return (T) bean;
}

源码中出现了两个getSingleton方法,第一个getSingleton的功能是从缓存中获取bean。如果这个bean是第一次被实例化,那就一定获取不到。第二个 的功能在于创建这个bean。所以这段代码整体的逻辑就是,如果缓存中存在这个bean,则直接返回,如果没有,则创建一个新的实例。 

这里咋一看,好像和循环依赖并没有太大的关系,但是深入看看第一个getSingleton方法的实现,发现其并不是从spring容器中取对象这么简单。

public Object getSingleton(String beanName) {
    //调用另一个重载的getSingleton方法
    return getSingleton(beanName, true);
}


protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //首先尝试从spring容器中获取bean实例
    Object singletonObject = this.singletonObjects.get(beanName);
    //如果容器中没有,且这个单例bean处于正在创建状态
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //从一个早产的单例对象池中获取bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            //如果依然没有,且允许被提前引用的话
            if (singletonObject == null && allowEarlyReference) {
                //从单例工厂池中获取到一个对象工厂
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //通过对象工厂构建单例对象
                    singletonObject = singletonFactory.getObject();
                    //将构建出的对象,放入earlySingletonObjects中
                    //显然earlySingletonObjects是一个缓存,下次可以就直接冲这个容器中拿到对象,而不需要工厂再次构建
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

重点看第二个重载方法的实现。spring显然不只是单纯的从单例池中拿对象。在spring容器中没有这个bean存在,且isSingletonCurrentlyInCreation判断为true时,spring容器会尝试从singletonFactories获取一个工厂并通过这个工厂构建出一个对象。

那么问题就来了

  1. 何时这个bean被标记成了正在创建状态
  2. singletonFactories这个个容器中装的是什么,是何时注入内容的
  3. singletonFactories中获取的工厂构建出的对象是什么。

顺着这个线索,我们继续看第二个 getSingleton 方法。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

    synchronized (this.singletonObjects) {
        //避免多线程问题,又一次尝试从单例池中获取
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            //创建的前置处理,将这个bean标记为正在创建状态
            beforeSingletonCreation(beanName);

            try {
                //调用传入工厂的getObject方法,创建对象
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {......}
            catch (BeanCreationException ex) {......}
            finally {......}
        }
        return singletonObject;
    }
}

protected void beforeSingletonCreation(String beanName) {
    //将bean标记为正在创建状态
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}

这个getSingleton方法主要功能就是调用传入的单例工厂中的getObject方法去创建这个bean的实例。并在创建实例之前,把这个bean标记为正在创建状态(装入singletonsCurrentlyInCreation容器中)。

所以当一个bean第一次被创建时,他并不是正在创建状态。进而会进入到第二个getSingleton方法中,创建这个bean的新实例,并被标记为正在创建状态。

上面提到的第一个问题已经解决,现在进入到bena的创建过程,寻找后两个问题的答案。

来到 doCreateBean方法,这个依然只贴出与循环依赖相关的代码

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

    //创建这个bena的实例
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    //获取到bena的实例,这里的实例是半成品的bean,还没有被注入过属性
    final Object bean = instanceWrapper.getWrappedInstance();


    //bean是单例的,且是正在创建的,且allowCircularReferences为true
    //前两点确定为true了,allowCircularReferences是在本类中初始值为true的一个属性
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences
        && isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        //这里就向singletonFactories中添加了属性
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    try {
        //装配bean,这里会自动注入bean的所有依赖
        //若依赖在spring容器中不存在,则先创建这个依赖实例
        populateBean(beanName, mbd, instanceWrapper);
    }        
}


protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        //再次确认了单例池中没有这个bean
        if (!this.singletonObjects.containsKey(beanName)) {
            //向singletonFactories中注册了传入的工厂
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

可以看到,单例bena在实例化成为半成品后,向singletonFactories中注册了一个对象工厂,并通过lambda表达式,将工厂中getObject方法的实现指向到了getEarlyBeanReference方法。

现在来重新回顾一下A和B两个类互相依赖时,这两个类的实例化过程。

当A第一次被创建时,他并不是正在创建状态。进而会进入到第二个getSingleton方法中,创建这个bean的新实例,并被标记为正在创建状态。 A的实例创建完毕后,向singletonFactories中注册了一个对象工厂,然后尝试自动注入B。此时的B实例在spring容器中并不存在,则调用getBean方法获取B的实例。

由于B也是第一次被创建,则相同的步骤进到第二个getSingleton方法中。被标记为正在创建状态后,创建了B的实例,向singletonFactories中注册一个对象工厂,然后B又开始尝试自动注入A对象。A此时还没完成属性的注入,所以spring容器中找不到。就再次调用getBean方法获取A的实例。

这次则是A的第二次创建过程,他已经被标记为正在创建状态,且在singletonFactories中有他注册的对象工厂。所以在第一个getSingleton方法中,就能获取到A注册的对象工厂,进而获取到其生产的对象。并将这个对象作为A的实例返回。

B获取到A的实例后,B的装配过程顺利完成。进行初始化之后,B成为一个真正的bean。此时A也就获取到了B的实例,进而A的实例化也完成了。

到了这里,循环依赖的死循环问题就已经解决了。但还有个问题没解决,getEarlyBeanReference方法给我们返回的是一个什么样的对象,这个对象为何可以代替真实的bean。

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    //注意,这里传入的bean是半成品bean
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                //掉用getEarlyBeanReference方法获取
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}


public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
    //直接就把半成品bean返回了
    return bean;
}

到了这里就是真破案了。在第二次获取A的实例时,第一个getSingleton方法返回的就是A的半成品对象。

由于在所有bean都是单例的情况下,A中依赖的B对象和B本身是同一个对象,B中依赖的A对象和A本身也是同一个对象。

把A的半成品注入给B,B完成了实例化过程。此时A所依赖的B就是这个实例化完成的B,A也就完成了实例化。有点绕。

换一种情况,如果我们在原型bean的情况下,每次调用getBean方法返回的bean都应该是一个全新的bean。第二次获取A时,就没办法将第一次的半成品返回,这个死循环也就将一直进行下去。

还有一种情况,当我们不是属性注入,而是构造器注入时。我们在对象的创建阶段就会被卡住,就更不谈半成品bean的返回了,所以构造器注入的循环依赖也是无法解决的。

最后是spring解决循环依赖的流程图

spring el 关联bean_spring_02