单例bean循环依赖解决
这版有错误 递归依赖加载并不是在这个循环里面,而是在填充属性的时候进行加载的。
循环依赖定义
在spring的启动过程中,有时候会出现bean之间的循环依赖,那什么是循环依赖呢?就是A依赖B,B依赖A,在A未加载完成的情况下,同时B开始了加载,这个时候又要注入A,这样就会出现问题。
SPRING 对循环依赖的解决
spring对单例的bean有循环依赖的解决方案,但是目前对原型模式下的bean的循环依赖并没有很好的解决方案。spring对单例bean 是采用提前在内存缓存中放置ObjectFactory来提前曝光bean,从而来解决循环依赖的问题的。
以前知道有这么一回事,但是没从源码层面去剖析,所以似懂非懂,这样不像是一位技术的master。 这里我对spring源码进行了简化。代码如下:
AbstractBeanFactory doGetBean 方法
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
//从缓存中尝试拿一遍bean 这个 getSingleton 方法里面有 singletonFactories 内存map 用来存储未加载好的bean 对应的ObjectFactory
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 处理FactoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
try {
//递归加载依赖的bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
if (mbd.isSingleton()) {
// 重载的getSingleton方法, 第二个参数是回调,在执行这个方法的时候 会执行createBean 方法,在这个方法里面,ObjectFactory被提前曝光了。
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
// 处理FactoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
return (T) bean;
复制代码
在以上这段代码里面,关键的注释已经被给了出来。我们看看这段代码做了什么事情。
第一步: 先从缓存里面拿单例bean,如果拿不出来,那么再进行下面步骤的加载。如果拿的出来,那么对它进行FactoryBean的处理。
第二步: 递归加载依赖的bean。 这个for循环被我简化了很多,如果有兴趣的朋友可以看看原来的样子,原来的样子中有个抛异常的代码还是非常值得学习的。
第三步: 递归到底层,如果是单例bean,那就调用重载的getSingleton方法,这个方法的第二个参数是回调,在回调creatBean方法调用的doCreatBean中判断这个bean是否还是在创建状态中,如果是,那就把ObjectFactory提前曝光。这个提前曝光的代码层次有点深,可以点进源码看一下。
第一个getSingleton方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject =null;
//这个单例是不是还在被创建,如果是,那么就去取singletonFactories 中的 ObjectFactory
if (isSingletonCurrentlyInCreation(beanName)) {
if (allowEarlyReference) {
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
}
}
}
return singletonObject;
}
复制代码
看看以上代码做了什么:
这些代码也被我简化过,主要还是从ObjectFactory池中拿ObjectFactory。
重载的getSingleton方法
public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//这个方法很重要,比较关键
beforeSingletonCreation(beanName);
try {
//执行回调
singletonObject = singletonFactory.getObject();
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
复制代码
第二个重载的方法比较重要同样也被我简化了,在beforeSingletonCreation 方法里面,我们在singletonsCurrentlyInCreation 这个set里面加入了beanName,标志这个bean正在被创建。那么执行到回调方法singletonFactory.getObject()的时候就开始提前曝光ObjectFactory了。我们可以来看看。
回调时调用的createBean方法的doCreateBean方法中有如下代码:
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");
}
//提前曝光 ObjectFactory
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
复制代码
我们可以看到earlySingletonExposure 布尔类型中有isSingletonCurrentlyInCreation方法,这个方法刚好也是根据上一步来的,看是否这个bean正在创建中。那么如果是的,就提前曝光ObjectFactory。
所以,这样spring就解决了单例模式下的循环依赖。有兴趣的朋友可以看看spring的源码。