spring是如何解决循环依赖的?

循环依赖就是N个类中循环嵌套引用,若是在平常开发中咱们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错。下面说一下Spring是若是解决的。spring

首先,须要明确的是spring对循环依赖的处理有三种状况: ①构造器的循环依赖:这种依赖spring是处理不了的,直 接抛出BeanCurrentlylnCreationException异常。 ②单例模式下的setter循环依赖:经过“三级缓存”处理循环依赖。 ③非单例循环依赖:没法处理。缓存

spring单例对象的初始化大略分为三步:bash

  1. createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
  2. populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
  3. initializeBean:调用spring xml中的init 方法。

从上面讲述的单例bean初始化步骤咱们能够知道,循环依赖主要发生在第1、第二步。也就是构造器循环依赖和field循环依赖。 接下来,咱们具体看看spring是如何处理三种循环依赖的。ui

一、构造器循环依赖

this .singletonsCurrentlylnCreation.add(beanName)将当前正要建立的bean 记录在缓存中 Spring 容器将每个正在建立的bean 标识符放在一个“当前建立bean 池”中, bean 标识 柏:在建立过程当中将一直保持在这个池中,所以若是在建立bean 过程当中发现本身已经在“当前 建立bean 池” 里时,将抛出BeanCurrentlylnCreationException 异常表示循环依赖;而对于建立 完毕的bean 将从“ 当前建立bean 池”中清除掉。this

二、setter循环依赖

Spring为了解决单例的循环依赖问题,使用了三级缓存。spa

/** Cache of singleton objects: bean name –> bean instance */
private final Map singletonObjects = new ConcurrentHashMap(256);
/** Cache of singleton factories: bean name –> ObjectFactory */
private final Map> singletonFactories = new HashMap>(16);
/** Cache of early singleton objects: bean name –> bean instance */
private final Map earlySingletonObjects = new HashMap(16);
复制代码

这三级缓存的做用分别是:prototype

singletonFactories : 进入实例化阶段的单例对象工厂的cache (三级缓存)code

earlySingletonObjects :完成实例化可是还没有初始化的,提早暴光的单例对象的Cache (二级缓存)xml

singletonObjects:完成初始化的单例对象的cache(一级缓存)对象

咱们在建立bean的时候,会首先从cache中获取这个bean,这个缓存就是sigletonObjects。主要的调用方法是:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    //isSingletonCurrentlyInCreation()判断当前单例bean是否正在建立中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            //allowEarlyReference 是否容许从singletonFactories中经过getObject拿到对象
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    //从singletonFactories中移除,并放入earlySingletonObjects中。
                    //其实也就是从三级缓存移动到了二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
复制代码

从上面三级缓存的分析,咱们能够知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache。这个cache的类型是ObjectFactory,定义以下:

public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}
复制代码

这个接口在AbstractBeanFactory里实现,并在核心方法doCreateBean()引用下面的方法:

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);
        }
    }
}
复制代码

这段代码发生在createBeanInstance以后,populateBean()以前,也就是说单例对象此时已经被建立出来(调用了构造器)。这个对象已经被生产出来了,此时将这个对象提早曝光出来,让你们使用。

这样作有什么好处呢?让咱们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的状况。A首先完成了初始化的第一步,而且将本身提早曝光到singletonFactories中,此时进行初始化的第二步,发现本身依赖对象B,此时就尝试去get(B),发现B尚未被create,因此走create流程,B在初始化第一步的时候发现本身依赖了对象A,因而尝试get(A),尝试一级缓存singletonObjects(确定没有,由于A还没初始化彻底),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,因为A经过ObjectFactory将本身提早曝光了,因此B可以经过ObjectFactory.getObject拿到A对象(虽然A尚未初始化彻底,可是总比没有好呀),B拿到A对象后顺利完成了初始化阶段一、二、3,彻底初始化以后将本身放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成本身的初始化阶段二、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,并且更加幸运的是,因为B拿到了A的对象引用,因此B如今hold住的A对象完成了初始化。

三、非单例循环依赖

对于“prototype”做用域bean, Spring 容器没法完成依赖注入,由于Spring 容器不进行缓 存“prototype”做用域的bean ,所以没法提早暴露一个建立中的bean 。