飞道的博客

图解Spring源码之循环依赖

415人阅读  评论(0)


Spring默认在 单例非构造方法注入的情况下是支持循环依赖的。

准备工作




关闭循环依赖

Spring 提供了关闭循环依赖的方法。

boolean allowCircularReferences

开始调试


我们从 doGetBean 开始分析。

AbstractBeanFactory#doGetBean

Spring 在初始化和 getBean() 时,都会调用这个方法。
源码概览
//... 表示被我省略掉的一些代码

	@SuppressWarnings("unchecked")
	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) {
			//...
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
			//...
			try {
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				
				//...
				
				// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// Check if required type matches the type of the actual bean instance.
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isTraceEnabled()) {
					logger.trace("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

代码很长,我们节选一些片段来解析

先检查一下单例池当中有没有手动注册的单例对象:

单例池:
Spring 中所有实例化好的单例 Bean 都存放在这个map当中。

判断当前的类是否在正在创建的原型集合当中,如果是,则抛异常:

说明一般情况下,原型不支持循环依赖。

prototypesCurrentlyInCreation:

判断当前 Bean 是否是单例,如果是,则把 Bean 创建出来。

getSingleton(String beanName, ObjectFactory<?> singletonFactory)


分析与循环依赖有关的代码片段:

判断当前实例化的 Bean 是否在正在被销毁:

singletonsCurrentlyInDestruction:

beforeSingletonCreation(beanName):

判断当前 Bean 是否正在被创建。因为 Spring 不管创建 Prototype Bean 还是 Singleton Bean,当他需要正式创建 Bean 的时候他会将这个 Bean add 到一个Set 中去

  • this.inCreationCheckExclusions.contains(beanName)判断当前需要创建的 Bean 是否在 Exclusions 集合中,程序员可以提供一些 Bean 不被 Spring 初始化(哪怕被扫描到了,也不初始化),那么这些被提供的 Bean 便会存在这个集合当中;
  • this.singletonsCurrentlyInCreation.add(beanName)把当前正在创建的 Bean 添加到正在创建的集合中。

singletonsCurrentlyInCreation:

继续往下执行到singletonObject = singletonFactory.getObject();

这里的 singletonFactory 就是上面传进来的那个 Lamda 表达式,我再贴一下:

所以我们要进到 createBean 方法中去。

从调用栈中我们也可以看出来这点:

AbstractAutowireCapableBeanFactory#createBean(beanName, mbd, args)


这里主要是 doCreateBean 方法:
调用 doCreateBean 后,x 和 y 都完成实例化了(循环依赖也完成了):

doCreateBean

下面着重分析一下 doCreateBean 这个方法,上面我们分析过一个名字类似的叫 doGetBean ,别搞混了:

createBeanInstance(beanName, mbd, args):

运行完以后,x 的构造方法就被调用了:
但是此时 x 对象只是被创建了,它还不是 Spring Bean,它的生命周期才刚刚开始。

往下执行到 earlySingletonExposure 变量赋值处:

  • mbd.isSingleton() 我们的 Bean 都是单例的,所以此处为 true
  • this.allowCircularReferences 默认允许循环依赖,此处为 true
  • isSingletonCurrentlyInCreation(beanName) x 在正在创建的 Bean 的集合中,此处为 true

这里又印证了上面我们说的关闭循环依赖的方法。

继续向下执行,这里又是一个 Lamda 表达式:

addSingletonFactory(beanName,singletonFactory) 添加一个单例工厂,这个工厂可以产生我们需要的半成品的 Bean 。

addSingletonFactory

下面这几行代码都很重要,依次分析下这几行代码:

先判断单例池 singletonObjects 中是否已经存在这个 Bean ,如果存在说明循环依赖已经完成。这里,我们称 singletonObjects一级缓存

如果一级缓存中不存在,将工厂对象 put 到 singletonFactories 中去,我们称之为二级缓存

earlySingletonObjects 中移除这个 Bean ,我们称 earlySingletonObjects三级缓存

先记住这三个缓存,我们先往下看,后面再来分析他们。

populateBean 这个方法完成了属性注入:

我们观察一下这行代码执行前后:
执行前:
只实例化了 x,y 还没实例化

执行后:
x 注入 y ,y 也注入了 x ,循环依赖完成!

然后绕来绕去又回到了 getSingleton:

但是这个 getSingleton 方法是我们上面看到的那个的兄弟(重载):

下面分析下这个兄弟:

  • 按照惯例还是先从单例池中获取(一级缓存)x,前面说过获取不到
  • 然后判断 x 是否在正在创建Bean 的集合当中,前面分析过这个集合现在存在 x y
  • if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) 成立进入分支
  • singletonObject = this.earlySingletonObjects.get(beanName); 从三级缓存中获取 x,根据前面的分析三级缓存当中现在什么都没有,故而返回nll
  • if (singletonFactory != null) 成立,进入分支
  • ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 从二级缓存中获取一个 ObjectFactory 工厂对象,二级缓存中存在创建 x 的工厂,故而可以获取到
  • singletonObject = singletonFactory.getObject(); 拿到一个半成品的 x Bean
  • this.earlySingletonObjects.put(beanName, singletonObject); 把 x 放到三级缓存,
  • this.singletonFactories.remove(beanName); 把二级缓存中 x 清除
  • 二级缓存中只存在一个 y 了,而三级缓存中多了一个 x

总结

最后使用一张图来总结一下

参考资料


转载:https://blog.csdn.net/AlphaBr/article/details/105910792
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场