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的整个过程。
在此过程中涉及到了三个缓存:
/** 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中的属性都互相依赖对方时,以一系列的图来演示流程:
- 创建实例A,实例化之后,立即将早期工厂放入到三级缓存singletonFactories中;
此时的三级缓存中的情况:
缓存 | 数据 |
---|---|
singletonObjects | |
earlySingletonObjects | |
singletonFactories | A |
- 对A进行属性填充,发现依赖B,需要getBean(B)。
- 创建实例B,实例化之后,立即将早期工厂放入到三级缓存singletonFactories中;
此时的三级缓存中的情况:
缓存 | 数据 |
---|---|
singletonObjects | |
earlySingletonObjects | |
singletonFactories | AB |
- 对B进行属性填充,发现依赖A,需要getBean(A)。
- getSignleton方法会依次从三层缓存中找A,在singletonFactories中获取到A,然后把它放到二级缓存中。此时认为已经得到了A的早期实例(也就是还没有进行属性注入的实例)
缓存 | 数据 |
---|---|
singletonObjects | |
earlySingletonObjects | A |
singletonFactories | B |
- 接第4步,继续对B进行属性填充,完成之后进行B的初始化,最后addSignleton(B)
缓存 | 数据 |
---|---|
singletonObjects | B |
earlySingletonObjects | A |
singletonFactories |
- 接第2步,继续对A进行属性填充,完成之后进行A的初始化,最后addSignleton(A)
缓存 | 数据 |
---|---|
singletonObjects | BA |
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不会被缓存,循环依赖也不可解决。