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

在Spring 的使用的时候 可能会碰到一下的场景


  



    

a-->b 即a依赖于b , b--> a即 b依赖于a

Spring提供构造器注入和Setter注入,那么也就可能造成构造器循环依赖和setter循环依赖。

构造器注入

    


    


    

此时构造器 形成了循环依赖
1.Spring对于构造器的循环依赖并没有解决 而是通过抛出异常
在创建A的时候发现,首先去 当前 "当前创建的Bean池",查找当前bean是否在创建,如果没有发现,则继续准备需要的构造参数B,并将A 放入到当前正在创建的Bean池。
2.创建B的时候发现,首先去 当前 "当前创建的Bean池",查找当前bean是否在创建,如果没有发现,则继续准备需要的构造参数C,并将B 放入到当前正在创建的Bean池。
3.创建C的时候发现,首先去 当前 "当前创建的Bean池",查找当前bean是否在创建,如果没有发现,则继续准备需要的构造参数A,并将A 放入到当前正在创建的Bean池
4.再次创建A的时候,发现A已近在当前创建的Bean池,表示循环依赖,则抛出beanCurrentLyInCreationException

Setter注入

1.Spring容器在创建单例“A”的时候,根据无参构造器,创建bean,并且暴露出ObjectFactory,用于返回一个提前暴露一个创建中的Bean,并将“A” 放入到当前创建的Bean池中,
2.Spring容器在创建单例“B”的时候,根据无参构造器,创建bean,并且暴露出ObjectFactory,用于返回一个提前暴露一个创建中的Bean,并将“B” 放入到当前创建的Bean池中,
3.Spring容器在创建单例“C”的时候,根据无参构造器,创建bean,并且暴露出ObjectFactory,用于返回一个提前暴露一个创建中的Bean,并将“C” 放入到当前创建的Bean池中,然后通过setter注入A,由于创建A的时候提前暴露了ObjectFactory,从而可以返回提前暴露的一个Bean.
4.最后通过依赖注入,A和B ,完成setter注入

源码分析

    //beanName --> bean instance
    private final Map singletonObjects = new ConcurrentHashMap(256);
    //beanName -->ObjectFactory  用于解决循环依赖 临时变量
    private final Map> singletonFactories = new HashMap(16);
    //beanName --> bean instance 用于解决循环依赖 临时变量
    private final Map earlySingletonObjects = new HashMap(16);

singletonFactories : 用于保存当前BeanName和 对象工厂的引用 一旦当前对象被创建singletonFactories 则BeanName 信息被删除
earlySingletonObjects :用于保存早期BeanName和bean实例的引用,也是当前Bean 一旦被创建 earlySingletonObjects 信息将被删除

下面介绍方法一

 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);//将当前Objectory 添加到singletonFactories 
                this.earlySingletonObjects.remove(beanName);//同时保证当前earlySingletonObjects 没有
                this.registeredSingletons.add(beanName);
            }
        }
    }

方法二

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //通过beanName获取单例bean
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            Map var4 = this.singletonObjects;
            synchronized(this.singletonObjects) {
                //通过beanName获取BeanFacotory
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject != NULL_OBJECT ? singletonObject : null;
    }

方法三

  protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

在方法一中:对象信息对beanFactory的形式被放入singletonFactories中,这时earlySingletonObjects中肯定没有此对象(因为remove)。

在方法二中:经过过一些条件 allowEarlyReference 为true的时候,bean的信息 就从singletonFactory工厂中拿出来,被放入到earlySingletonObjects 同时 singletonFactories 调用remove 掉 也就是说earlySingletonObjects 和 singletonFactories是一个互斥的变量

在方法三中:在方法3中,对象被加入到singletonObjects中,同时singletonFactories和earlySingletonObjects中都remove掉持有的对象(不管持有与否),这就表示在之前的处理中,这只相当于一个临时容器,处理完毕之后都会remove掉。

在正常的情况下,调用顺序如下:以下有无,表示是否持有对指定Bean的引用

singletonFactories earlySingletonObjects singletonObjects
getSingleton(beanName, true)
doCreateBean(beanName,mdb,args)
getSingleton(beanName, true);
addSingleton(beanName, singletonObject)

如果出现循环依赖 解决调用顺序如下

singletonFactories earlySingletonObjects singletonObjects
getSingleton(A, true) A无B无 A无B无 A无B无
doCreateBean(A,mdb,args) A有B无 A无B无 A无B无
populateBean(A, mbd, instanceWrapper) 填充属性A ,同时由A解析B……
getSingleton(B, true) A有B无 A无B无 A无B无
doCreateBean(B,mdb,args) A有B有 A无B无 A无B无
populateBean(B, mbd, instanceWrapper) 填充属性B,由B准备解析A……
getSingleton(A, true) A无B有 A有B无 A无B无
完成populateBean(B, mbd, instanceWrapper)解析……
addSingleton(B, singletonObject) A无B无 A有B无 A无B有
完成populateBean(A, mbd, instanceWrapper)
A- = initializeBean(beanName, exposedObject, mbd)在initializeBean之后A变为A-
getSingleton(A, false);验证 addSingleton(A, singletonObject) ……

在上面这个过程中,在对A进行验证时,就会从earlySingletonObjects中取得一个A,但是这个A和后面的A-可能不是同一个对象,这是因为有了beanPostProcessor存在,它可以改变bean的最终值,比如对原始bean进行封装,代理等。在这个过程中,出现了3个对象A,A-,B,而B中所持有的A对象为原始的A。如果这里的A和A-不是同一个对象,即产生了beanA有了beanB的引用,但beanB并没有beanA的引用,而是另一个beanA的引用。这肯定不满足条件。

那么我们看Spring 对这种情况的处理:

Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {//判断点1
                if (exposedObject == bean) {//判断点2
                    exposedObject = earlySingletonReference;
                }
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {判断点3
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set actualDependentBeans = new LinkedHashSet(dependentBeans.length);
                    for (String dependentBean : dependentBeans) {
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    if (!actualDependentBeans.isEmpty()) {判断点4
抛出对象不致异常。
}

判断点1,首先确定这个对象能从earlySingletonObjects中取出对象来,经过上面的分析,我们知道,在正常情况下,此对象为null,即不存在循环检测。而在循环引用中,此对象能够被取出来。

判断点2,再判断这个对象和当前通过beanPostProcessor处理过的对象是否相同,如果相同,表示对象没有经过修改,即A=A-,那么循环引用成立。无需处理

判断点3,判断当前对象A是否被其他对象所依赖,在循环引用中,已经处理了A和B,那么在依赖表中,即在属性dependentBeanMap和dependenciesForBeanMap中。其中A->B表示A依赖于B,B->A表示B依赖于A。那么在dependentBeanMap中就会出现两个entry,分别为A->B和B->A。这里A依赖于A,那么表示A已经被依赖,则进入进一步检测中。在检测中,将取得一个A的被依赖列表中的bean已经被创建的对象列表值。

判断点4,如果被依赖对象列表不为空,则表示出现循环引用。因为按照创建规则,如果A->B,则必须先创建B,而B->A,则必须先创建A。在这里,A被B依赖,就要求A必须在B之前被创建,而B又被A依赖,又要求A必须在B之前被创建。这创建的两个对象必须满足一致才可以。即在A->B中的两个对象,必须和B->A的两个对象,互相一致才可以,否则就不是循环引用。

至此,整个流程梳理清楚。那么,如何处理这种循环引用呢?答案其实也很简单,在xml中将两方的循环切掉。然后使用一个beanPostProcessor即可以,此beanPostProcessor必须要在放到所有beanPostPrcessor的最后面。然后此beanPostProcessor,这样写即可:

  判断当前bean为beanA
BeanB beanB=beanFactory.getBean(“beanB”);
beanA.setBeanB(beanB);
beanB.setBeanA(beanA);

想具体了解的 可以看看一下博客
Spring 循环依赖详细介绍

你可能感兴趣的:(Spring是如何解决循环依赖的?)