spring getBean及循环依赖基本流程分析

前言

  本文基于大佬 文章并结合个人理解进行了一个简单的流程整理,仅供参考,欢迎交流。

一、Bean的三级缓存及简单bean的实例化过程

  在bean的实例化过程中,spring使用了3个缓存,这3个缓存是互斥的,即其中一个存有bean的缓存则其他2两个便没有。具体定义如下:

/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
	
	/** Set of registered singletons, containing the bean names in registration order. */
	private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

  按个人理解,我将singletonObjects称为3级缓存、earlySingletonObjects称为2级缓存、singletonFactories称为1级缓存。后文将按此定义进行描述。当bean被完全实例化后其将被存入三级缓存。源码如下:

protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

  当用容器初始化完成后会去对非延迟加载的bean进行注册,刚开始时会尝试从这三个缓存中获取bean的实例。源码如下:

@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

  如源码所示,其会根据一定的条件依次从3、2、1级缓存中去获取bean。当第一次尝试获取的时候getSingleton(beanName, true)肯定是拿不到bean的,那么它会进入创建bean的流程。在进入bean创建前,还有一个重要步骤就是将这个beanName加入singletonsCurrentlyInCreation供后续getSingleton使用。接着核心流程将会为bean 创建一个1级缓存对象ObjectFactory供后续使用,改2级缓存对象将不会被添加到1级缓存,而是直接使用。源码如下:

// 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;
						}
					});
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

  这里的lamda表达式就是ObjectFactory的匿名实现。接着核心流程将调用这个匿名类——singletonFactory.getObject(),并进入doCreateBean流程,该方法是真正创建bean的地方。在此方法中,首先会创建bean的实例,此时的bean实例的属性还未初始化,还是一个比较原始的bean,然后再为bean创建一个1级缓存对象ObjectFactory并将其添加1级缓存中,这个1级缓存对象在解决循环依赖的时候才会被取出并执行它的getObject方法。源码如下:

// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		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");
			}
			addSingletonFactory(beanName, () -> 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的属性,如果没有循环依赖在后面调用getSingleton(beanName, false)时将还是拿不到bean,也就不会继续后面的循环依赖检测。源码如下:

@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		Object singletonObject = this.singletonObjects.get(beanName);// null
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// true
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {// false
			...
			return null;
if (earlySingletonExposure) {// true
			Object earlySingletonReference = getSingleton(beanName, false); // null
			if (earlySingletonReference != null) {// false 循环依赖检测
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}

  后面便是对bean的实例化完成并进行bean的注册,也即最开始的addSingleton方法。根据个人理解对总的流程进行了如下总结:
在这里插入图片描述

二、循环依赖

  按个人理解spirng能解决循环依赖的巧妙设计在于2阶段的bean实例化和3级缓存。现假设存在A->B,B->A两个对象相互依赖。A定义在前,A实例化后去初始化属性的时候会去找B的实例,接着B又开始实例化,B实例化的时候去找A的引用。如上文所述A初始化时会添加一个1级缓存对象ObjectFactory,这个对象在B初始化时又去获取A的时候就会被获取到并调用getObject方法。这个时候,获取到A的原始版本并被添加到了2级缓存中。B初始化完成后,那么就能被赋值到A的属性中,A的属性被初始化后就进入A初始化后期对循环依赖的检测流程。在对A进行循环依赖检测的时候,会再次调用getSingleton(beanName, false),此时就能拿到2级缓存里面的A,如果A在前面的被initializeBean代理增强过那么循环依赖检测就会抛异常,如果没有则接着后面的流程完成bean的实例化。
循环依赖检测的代码如下:

if (earlySingletonExposure) {// true
			Object earlySingletonReference = getSingleton(beanName, false);// 循环依赖时 2级缓存中取出A
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {// 增强后 false
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {// 增强后抛异常
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

  spring只支持setter注入的singleton循环依赖:1、prototype不是单例不使用三级缓存,只缓存了beanName作为循环依赖判断。2、构造器注入在第一阶段createWrappedInstance时发生,到达不了三级缓存循环依赖的处理的流程。
  发生循环依赖,spring的大致处理流程如下:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值