Open Source, Open Future!
  menu
107 文章
ღゝ◡╹)ノ❤️

spring---循环依赖

简介

如下图所示,A中引用了B,B中引用了A,这样的两个以上的bean之间互相引用的情况就叫循环依赖。
image.png

测试

测试代码
public class A {

    private B b;

    public A() {
    }

    public A(B b) {
        this.b = b;
    }

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}
public class B {

    private A a;

    public B() {
    }

    public B(A a) {
        this.a = a;
    }

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }
}
    @Test
    public void test19() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        A a = (A) context.getBean("a");
        B b = (B) context.getBean("b");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a.getB() == b);
    }
构造器注入
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="a" class="mncode.A">
        <constructor-arg ref="b"/>
    </bean>
    <bean id="b" class="mncode.B">
        <constructor-arg ref="a"/>
    </bean>
</beans>

会报异常:BeanCurrentlyInCreationException

属性注入

修改配置文件:

    <bean id="a" class="mncode.A">
        <property name="b" ref="b"/>
    </bean>
    <bean id="b" class="mncode.B">
        <property name="a" ref="a"/>
    </bean>

输出结果:

mncode.A@42eca56e
mncode.B@52f759d7
true
prototype模式

Spring中的bean默认为singleton模式,如果改为prototype模式,如下所示:

    <bean id="a" class="mncode.A" scope="prototype">
        <property name="b" ref="b"/>
    </bean>
    <bean id="b" class="mncode.B" scope="prototype">
        <property name="a" ref="a"/>
    </bean>

会报异常:BeanCurrentlyInCreationException

总结
  • Spring不能解决构造器循环依赖;
  • Spring不能解决prototype模式的循环依赖;
  • Spring可以解决singleton模式的属性循环依赖;

源码分析

依赖注入在第一次调用getBean方法时触发,时序图如下:

image.png

1、在第7步:doCreateBeanA实例化之后,有以下代码:

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		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");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

addSingletonFactory源码如下:

	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);
			}
		}
	}

A实例化之后放在了singletonFactories缓存中。

2、然后进入第10步:作属性注入时,发现A引用了B,使用getBean去获取B,在getSingleton方法中有以下源码:

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

先从singletonObjects缓存中获取,再从earlySingletonObjects缓存中获取,最后从singletonFactories缓存中获取,因为都没有,返回了null;所以需要去创建B,实例化之后同样也放在singletonFactories缓存中;
3、B的流程进入第10步作属性注入,发现引用了A,使用getBean去获取A,同上也是先从3个缓存中找,从singletonObjects缓存找到了;然后将AsingletonFactories缓存中移到earlySingletonObjects缓存中;
4、B的流程进入第13步,addSingleton源码如下:

	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

BsingletonFactoriesearlySingletonObjects缓存中移到singletonObjects缓存中。
5、B的创建流程结束,继续进行A的后半部分流程;
6、A的流程进入第13步,同上,AsingletonFactoriesearlySingletonObjects缓存中移到singletonObjects缓存中。
7、A的创建流程结束。

总结

  • Spring中使用了三级缓存的结构:singletonObjects(第一级)、earlySingletonObjects(第二级)、singletonFactories(第三级);
  • bean实例化后属性注入前会提前曝光,放入第三级缓存中,这样其他地方依赖这个bean时可以找到(虽然此时bean并不完整,但在堆中已经分配了空间,可以通过引用找到);
  • bean创建流程走完时会移到第一级缓存中,此时bean是完整的(其他地方引用的这个bean自然也会是完整的)。

备注

变量类型说明
singletonObjectsConcurrentHashMapbeanName ---> bean instance
earlySingletonObjectsHashMapbeanName ---> bean instance(这里的 bean 不是完整的)
singletonFactoriesHashMapbeanName ---> ObjectFactory
alreadyCreatedSetNames of beans that have already been created at least once
registeredSingletonsLinkedHashSet注册过的bean名称集合
singletonsCurrentlyInCreationSet正在创建中的bean名称集合
factoryBeanInstanceCacheConcurrentHashMapCache of unfinished FactoryBean instances: FactoryBean name to BeanWrapper