Spring-bean循环依赖
循环依赖指的是两个或以上bean相互存在引用。
Spring中循环依赖的几种情况:
1.构造器参数循环依赖;无法解决,只能避免
2.setter方式,单例;
3.setter方法,prototype原型;无法解决,只能避免
在bean初始化一文中,可以知道,创建bean需要经过一下几个步骤:
1.实例化bean;
2.populate bean,填充bean的属性;
3.initialize bean,初始化bean;
对于作用域为singleton的bean,创建过程中以及创建完成会缓存该bean;
从DefaultSingletonBeanRegistry的源码中可以看出:
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
//标记bean正在创建,创建完成后会remove
/** Names of beans that are currently in creation */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
spring容器通过三级缓存解决循环依赖问题。
容器在创建单例bean前,会先判断缓存中是否存在;
AbstractBeanFactory#doGetBean:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
//从缓存中获取
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
//不存在,创建bean
...
}
}
再看看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获取,如果有,则移到二级缓存earlySingletonObjects;
如果缓存没有,则创建bean。bean在实例化bean之后,会将bean添加到singletonObjects中,并将bean从singletonFactories和earlySingletonObjects中移除,DefaultSingletonBeanRegistry#addSingleton:
/**
* Add the given singleton factory for building the specified singleton
* if necessary.
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
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);
}
}
}
这是解决循环依赖的关键,该方法调用在createBeanInstance之后,populateBean之前,它的作用是提前暴露对象,但该bean未完成属性填充以及初始化。
比如A和B存在循环依赖,bean A在实例化后,将自己保存到singletonFactories,此时进行下一步填充属性,发现需要引用到B,接着容器创建bean B,在实例化bean B后,填充属性时发现依赖到A,此时尝试从容器中获取A,由于此时A初始化尚未完成,但是A提前暴露了对象,所以可以从singletonFactories中提前获取A对象引用,接着B完成自身初始化,完成后再返回A,继续进行属性填充及初始化。
从上面也可以知道,为什么构造器参数循环依赖不行,因为要先实例化bean。容器也不能解决原型bean的循环依赖问题,因为容器没有缓存原型bean,也就不能提前暴露对象,只能去避免。
参考
https://blog.csdn.net/u010853261/article/details/77940767
https://blog.csdn.net/u010644448/article/details/59108799#comments
还没有评论,来说两句吧...