Spring IOC(六)循环依赖的解决——Spring对于Bean的三级缓存

Singleton类型的Bean基于setter的循环依赖可解决,原理是spring中的三级缓存:
	一级存储完整Bean
	二级存早期实例
	三级存单例工厂
但是Singleton类型基于构造器的循环依赖不可解决;
不是Singleton类型的Bean循环依赖也不可解决,因为不会被缓存。

循环依赖,即两个或多个Bean的属性互相引用了对方,导致每个Bean都不能创建成功。

注意,在spring中说到循环依赖的解决方式,默认是指Singleton的Bean。
因为如果是Prototype模式下,每当一个BeanA中检查到依赖了另一个BeanB,就会去创建B,然后在B中又发现了A,如此循环创建。。。
当然spring拦截了这种可能,当检查到有依赖的情况时,直接抛出错误BeanCurrentlyInCreationException。具体检查方法,见Spring(二)doGetBean方法源码分析中第3步:Prototype类型Bean的循环依赖检查。

Singleton类型的Bean循环依赖分为了两种情况:

基于构造器的循环依赖

public class A {
    B b;

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

    public void testA() {
        System.out.println("testA");
    }
}
public class B {

    A a;

    public B(A a) {
        this.a = a;
    }
    
    public void testB(){
        System.out.println("testB");
    }
}

配置文件:

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

测试类:

public class MainTest {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        A a = ac.getBean(A.class);
        a.testA();
    }
}

运行结果:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'a' defined in class path resource [spring/applicationContext.xml]: Cannot resolve reference to bean 'b' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [spring/applicationContext.xml]: Cannot resolve reference to bean 'a' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:648)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:140)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1143)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
	at example.controller.MainTest.main(MainTest.java:10)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [spring/applicationContext.xml]: Cannot resolve reference to bean 'a' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:648)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:140)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1143)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
	... 17 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
	... 29 more

基于setter的循环依赖

public class A {
    B b;
    public B getB() {
        return b;
    }
    public void setB(B b) {
        this.b = b;
    }
    public void testA() {
        System.out.println("testA");
    }
}
public class B {
    A a;
    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
    public void testB(){
        System.out.println("testB");
    }
}

配置文件:

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

运行结果:

(正常输出)testA

spring获取Bean的流程

回顾获取Bean的整个过程。

获取Bean过程

在此过程中涉及到了三个缓存:

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
  • 一级缓存:单例对象缓存:当Bean已经被实例化、初始化之后,也就是完全创建完成后,会放入singletonObjects。
  • 二级缓存:早期单例对象缓存:它是一个中间的过渡缓存,保存已经被实例化,但没有被属性注入的早期暴露Bean。 当Bean没有完全创建完成时,一级缓存中当然没有,尝试二级缓存中获取,如果二级缓存中没有,但是在三级缓存中有此Bean的创建工厂,意味着可以得到这个Bean的早期暴露对象,将Bean的工厂从三级缓存中移除,放入二级缓存中。
  • 三级缓存:单例工厂缓存 :它保存的是可以创建Bean的工厂。

getSingleton
首先尝试从一级缓存中获取,没有的话再从二级缓存获取,还是没有,从三级缓存的工厂获取早期Bean,并且将得到的早期Bean放入二级缓存。

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 != NULL_OBJECT ? singletonObject : null);
	}

addSingletonFactory
将Bean的早期工厂加入三级缓存,从工厂中可以得到早期Bean实例。

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

addSingleton
此时的Bean已经完全的被创建完毕,从二级、三级缓存中移除,放入到一级缓存中。

/**
	 * Add the given singleton object to the singleton cache of this factory.
	 * <p>To be called for eager registration of singletons.
	 * @param beanName the name of the bean
	 * @param singletonObject the singleton object
	 */
	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);
		}
	}

setter方式循环依赖分步分析

当A与B中的属性都互相依赖对方时,以一系列的图来演示流程:

  1. 创建实例A,实例化之后,立即将早期工厂放入到三级缓存singletonFactories中;
    此时的三级缓存中的情况:
缓存数据
singletonObjects
earlySingletonObjects
singletonFactoriesA
  1. 对A进行属性填充,发现依赖B,需要getBean(B)。
  2. 创建实例B,实例化之后,立即将早期工厂放入到三级缓存singletonFactories中;
    此时的三级缓存中的情况:
缓存数据
singletonObjects
earlySingletonObjects
singletonFactoriesAB
  1. 对B进行属性填充,发现依赖A,需要getBean(A)。
  2. getSignleton方法会依次从三层缓存中找A,在singletonFactories中获取到A,然后把它放到二级缓存中。此时认为已经得到了A的早期实例(也就是还没有进行属性注入的实例)
缓存数据
singletonObjects
earlySingletonObjectsA
singletonFactoriesB
  1. 接第4步,继续对B进行属性填充,完成之后进行B的初始化,最后addSignleton(B)
缓存数据
singletonObjectsB
earlySingletonObjectsA
singletonFactories
  1. 接第2步,继续对A进行属性填充,完成之后进行A的初始化,最后addSignleton(A)
缓存数据
singletonObjectsBA
earlySingletonObjects
singletonFactories

问题

  • 为什么基于构造器的循环依赖不可解决?
    Bean的实例化分为三个情况:工厂方式实例化、无参构造器实例化、有参构造器自动装配实例化;当实例化完成之后才会将早期工厂加入三级缓存中。基于有参构造器的方式不能在缓存中得到依赖Bean的早期实例,不能完成Bean的实例化。
  • 为什么要有一个三级缓存,而不直接用二级缓存呢?
    如果在实例化完成一个Bean时,将其存入二级缓存,而不是将创建Bean的工厂存入三级缓存,貌似也可以实现解决循环依赖的效果。但是二级缓存存在的意义是,存放Bean的 代理 对象。从三级缓存取出Bean时,如果Bean有被代理,放入二级缓存中的是被代理过的早期对象。

在从第三层缓存singletonFactories中获取早期Bean实例时:

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 != NULL_OBJECT ? singletonObject : null);
	}

ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
singletonObject = singletonFactory.getObject();
而此早期工厂的getObject方法:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
					if (exposedObject == null) {
						return exposedObject;
					}
				}
			}
		}
		return exposedObject;
	}

这个方法中,调用SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference方法,这其中对AOP进行了判断处理。

总结

  • 构造器形式的循环依赖是不可被解决的;
  • setter形式的循环依赖可以被解决,解决方式是使用了三级缓存机制;
  • 不是单例模式的Bean不会被缓存,循环依赖也不可解决。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值