您的位置:首页 > 编程语言 > Java开发

spring 源码探索--单例bean解决循环依赖问题

2016-11-26 17:45 896 查看
spring 中循环依赖问题:

ItemA 依赖ItemB,ItemB依赖ItemC,ItemC依赖ItemA,这就造成了循环依赖。

循环依赖有两种实现方式:构造函数,setter注入

单例模式

构造函数

public ItemA(ItemB itemB){
this.itemB = itemB;
}


这种情况造成的循环依赖在spring中是无法解决的,只能报
BeanCurrentlyInCreationException


在spring中,使用类的构造的函数实例化bean之前,即
singletonObject = singletonFactory.getObject();
调用这个之前,会判断是否当前的bean是否正在创建,如果当前bean正在创建,则会报异常。如果不正在创建,则加入创建bean的池中进行标记。

setter注入

即通过getter/setter方法注入bean.

这种情况可以解决,通过当实例化完bean,还没完成初始化时,提前暴露出一个ObjectFactory,这个工厂正好返回的是刚刚完成实例化的bean.

暴露ObjectFactory工厂

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, 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);
}
}
}


首先我们需要先了解几个bean的缓存池的作用

1. singletonObjects :BeanName和创建bean实例之间的关系(注:不是最终返回的bean,可能是FatoryBean)

2. singletonFactores: BeanName和创建bean的工厂

3. earlySingletonObjects: BeanName和创建bean实例之间的关系,与1有所不同,bean实例还处于创建的过程中,可以通过getBean获取,用于检测循环引用。(ps:这个缓存池与2是互斥的)

4. registeredSingletons:用来保存所有已经注册的bean.

到了这里,已经暴露了正在创建中的bean的ObjectFactory。

接下来就来到了初始化bean,注入相关的依赖

populateBean(beanName, mbd, instanceWrapper);


在这里面会注入依赖的bean,当有循环引用出现,一定有一个终止的条件才能解决循环依赖,就像递归一样。

在最开始加载bean的时候,spring首先从缓存中读取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);
}


如果能在这几个缓存池拿到循环依赖的bean,那么循环依赖就解决了。

A -> B -> C -> A

假如一开始加载A对象,现在假如到了创建A的依赖C这个地方,那A现在的状态是刚刚完成构造函数的实例化,准备进行完成注入其他bean。这个时候创建他的ObjectFactory也已经加入到了
singletonFactores
,C这个时候去注入A,通过上面的
getSingleton
方法即可拿到刚刚实例化完的A对象。最后回到A对象,A继续完成其他属性的注入,此时C引用的A已经不再是刚刚实例化完的A对象了。整个依赖完成。

假设在上面的
getSingleton
方法上没有得到bean A,那异常
BeanCurrentlyInCreationException
会在再次创建bean A之前发生。

有一个正在创建bean池,会记录当前创建中的bean。

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


原型模式

无法解决循环依赖,因为spring容器不缓存prototype作用域的bean.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring