Spring源码分析---单例模式下循环依赖的解决

单例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的源码。

你可能感兴趣的:(java)