Spring中循环依赖

Spring中bean的实例化方法主要是 prototype和singleton两种。其中singleton的循环依赖包括两种:构造器循环依赖,和setter的循环依赖。prototype中的循环依赖。

1.singleton 构造器循环依赖

代码示例:

public class CycleA {

    private CycleB cycleB;
    public CycleA(CycleB cycleB) {
        this.cycleB = cycleB;
    }

    public void doSomething() {
        System.out.println("this is the CycleA ... ");
    }
}
public class CycleB {

    private CycleC cycleC;
    public CycleB(CycleC cycleC) {
        this.cycleC = cycleC;
    }

    public void doSomething() {
        System.out.println("this is the CycleB ...");
    }
}
public class CycleC {

    private CycleA cycleA;
    public CycleC(CycleA cycleA) {
        this.cycleA = cycleA;
    }

    public void doSomething() {
        System.out.println("this is the CycleC ...");
    }
}

配置文件示例:

<bean id="cycleA" class="com.test.bean.blog.myCycleDependencies.bean.CycleA">
        <constructor-arg name="cycleB" ref="cycleB"/>
    </bean>
    <bean id="cycleB" class="com.test.bean.blog.myCycleDependencies.bean.CycleB">
        <constructor-arg name="cycleC" ref="cycleC"/>
    </bean>

    <bean id="cycleC" class="com.test.bean.blog.myCycleDependencies.bean.CycleC">
        <constructor-arg name="cycleA" ref="cycleA"/>
    </bean>

这个是一个Singleton构造器循环依赖的实例。构造器的循环依赖无法解决。CycleA依赖于CycleB,CycleB依赖于CycleC 。CycleC依赖于CycleA。在创建的时候,当构造CycleC的时候需要去创建CycleA。这个时候这个时候需要判断出发生了循环。
关键点:一、当创建CycleC的时候,需要创建CycleA,是在哪里被触发的。二、如何发现CycleA是一个循环依赖的。

  • 如何发现CycleA是一个循环依赖的

可以看一下代码:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "'beanName' must not be null");
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                if (this.singletonsCurrentlyInDestruction) {
                    throw new BeanCreationNotAllowedException(beanName,
                            "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                            "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                }
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<Exception>();
                }
                try {
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                catch (IllegalStateException ex) {
                    // Has the singleton object implicitly appeared in the meantime ->
                    // if yes, proceed with it since the exception indicates that state.
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        throw ex;
                    }
                }
                catch (BeanCreationException ex) {
                    if (recordSuppressedExceptions) {
                        for (Exception suppressedException : this.suppressedExceptions) {
                            ex.addRelatedCause(suppressedException);
                        }
                    }
                    throw ex;
                }
                finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    addSingleton(beanName, singletonObject);
                }
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }
    }

以上代码是获取一个单例对象。首先Object singletonObject = this.singletonObjects.get(beanName); 获取到对应的bean。如果获取失败,就开始调用beforeSingletonCreation(beanName);开始创建。
代码如下:

protected void beforeSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}

其中由于CycleA在最开始创建的时候已经被写入到this.singletonsCurrentlyInCreation中了,这个beanName被写入到set中了。在创建过程中这个对象依赖到自身了,返回false。通过一个Set来记录下创建当前bean中依赖的bean。如果发现自己依赖自己就是一个循环依赖。

  • 当创建CycleC的时候,需要创建CycleA,是在哪里被触发的。

在通过CycleC的构造函数来创建bean的时候,发现会依赖CycleA的实例,这个时候首先创建CycleA的对象。具体代码如下:

protected BeanWrapper autowireConstructor(
            String beanName, RootBeanDefinition mbd, Constructor<?>[] ctors, Object[] explicitArgs) {

        return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
    }

将通过构造器来生成一个BeanWrapper的工作委托给ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs)。这个过程中需要将构造器中依赖的CycleA先实例化出来。
这里写图片描述
这个工作委托给BeanDefinitionValueResolver类中的public Object resolveValueIfNecessary(Object argName, Object value)方法
看下这个方法的实现由于在配置文件中定义了ref,在Spring内部被转换为RuntimeBeanReference对象:
这里写图片描述
具体的实现是通过BeeanFactory.getBean()来获取到所依赖的CycleA的实例。
具体如下第351行:
这里写图片描述

  • 总结
    以上就是Spring中单例构造器循环依赖的过程。在开始创建CycleA的时候,在准备实力化的时候将CycleA写到singletonsCurrentlyInCreation中,表示当前的bean对象开始实例化。
    然后开始创建的过程中,当将一个CycleA创建为一个BeanWrapper的时候,首先需要CycleA的构造函数中依赖的CycleB的对象,这个时候就开始创建CycleB,这个时候也会重复上面的过程将CycleB写到singletonsCurrentlyInCreation
    往复以上过程,又一次创建CycleA的时候,发现singletonsCurrentlyInCreation已经有了一个对象,表示一个对象循环依赖了。抛出异常。
2.prototype 循环依赖

代码示例还是之前的示例,将每个bean的scope改为prototype类型。
prototype循环依赖的处理过程如下,由于prototype只负责创建一个对象。循环依赖的判定逻辑是在创建CycleA的时候,发现依赖了CycleA,在多线程中,可能会出现这种情况,但是这种并不是循环依赖,所以使用ThreadLocal来隔离开不同的循环依赖。
在创建对象之前先检查是否是循环依赖了:
这里写图片描述
看下具体实现:
这里写图片描述
从ThreadLocal中获取到对应beanName是否在创建,如果在创建,就抛出异常。其中,当只有一个beanName的时候使用的String,当有多个beanName的时候存的是Set。

在创建之前将beanName的信息写到ThreadLocal中去:
这里写图片描述
和上面相同,需要判断是一个还是多个,多个需要将String转变为Set。

在创建完成的额时候需要将beanName从ThreadLocal中删除:
这里写图片描述

总结
prototype循环依赖主要是使用ThreadLocal来记录当前bean的依赖情况,创建之前先判断一下是否循环依赖,如果没有循环依赖将当前的beanName写到ThreadLocal中去。在创建完成的时候将当前beanName删除。

Singleton的setter循环依赖

相对于基于Singleton构造器循环依赖和prototype中判断当前bean是否正在创建set中,如果在就直接抛出异常。Singleton的setter循环依赖中判断是否是循环依赖也是用了和构造器相同的方法,但是将创建一个对象的过程拆分为两个步骤:initBeanpopuBeaninitBean:主要负责生成一个Bean的实例,这个实例中的属性可能还没有注入。popuBean向bean中注入属性,在获取属性的过程中可能会循环的去获取其他的bean。(Spring中对于属性的获取都是委托给BeanDefinitionValueResolver处理的。)
以 CycleA—> CycleB —> CycleC —> CycleA 。 这样一个循环依赖。这些加载的过程是如下过程:(注:initCycleA表示实例化CycleA;popuCycleA表示填充CycleA)。

这里写图片描述
以上:Spring在实例化的时候是一个递归的过程。在popuCycleA的时候,这个时候发起了initCycleB,然后开始popuCycleB,又递归的发起了initCycleC,然后popuCycleC,这个当中,由于CycleA已经初始化完成了,已经能够返回CycleA的实例。能够完成popuCycleC。然后递归的完成popuCycleB,然后完成popuCycleA。最终将所有的bean初始化完成。

Spring中在处理单例的时候所有的方法都是委托给DefaultSingletonBeanRegistry处理的。这个当中有三个关键的方法来帮助解决Spring中的循环依赖。

  • public Object getSingleton(String beanName)
    这个类是在BeanFactory.getBean最开始的地方调用的。
    这里写图片描述

按照注释,返回的实例是可能是一个完全实例化的bean也是一个为注入属性的bean。在DefaultSingletonBeanRegistry中不同的Bean将会被放在不同的容器中。这个后面再介绍。

  • public Object getSingleton(String beanName, ObjectFactory singletonFactory)

在这个方法中ObjectFactory中getObject方法提供了一个创建Bean的方法。

以上在createBeanean()是创建一个Bean的方法。createBean做了两件事,initBean和popuBean。在做这两件事情的中间调用了第三个接口

  • protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory)
    这里写图片描述
    如上图,这个方法调用的时机在initBean完成以后,在popuBean之前。这样在popuBean的过程中通过第一个方法获取到bean的实例的时候就能够获取到bean,而不用重新创建,触发循环校验的机制了。

Spring中单例的对象从创建到完成之间尽力的形态:
DefaultSingletonBeanRegistry中维护了三个Map来存放Bean
Map<String, Object> singletonObjects //缓存了所有的完全实例化成功的bean
Map<String, ObjectFactory<?>> singletonFactories //缓存了当前所有的ObjectFactory。这个ObejctFactorygetObject()中的方法是一个已经实例化的bean
Map<String, Object> earlySingletonObjects //这个bean中缓存的bean是一个已经实例化完成,但是还没有完全pop的bean。

总结
解决单例setter循环依赖的方式主要是将实例化bean的过程分为initBean 和popuBean过程,在这两个阶段之间调用一下第二个方法将bean注入到DefaultSingletonBeanRegistry中去。这样在循环依赖的时候就能够获取到依赖的对象的引用,而不用在一次去再一次initBean,发生循环依赖。

ObjectFactory这个接口是DefaultSingletonBeanRegsitry提供的一个获取bean的策略,这样能够使得类更加的通用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值