Spring源码分析——循环依赖

Spring源码分析——循环依赖

在spring中,涉及到一个概念,叫做循环依赖,或者叫做循环引用。意思就是说,一个bean他的属性,依赖了另外一个bean,而他所依赖的bean,有恰好依赖与他。这种情况下,spring对此做了特殊处理,但是仅限于单例模式。

具体流程

处理循环依赖的具体流程,首先是在一个bean实例化后初始化前,如果判断判断符合循环依赖的条件,则提前把他暴露出去,但是不是暴露这个bean,而是封装成一个ObjectFactory对象,存放到二级缓存singletonFactories中,他的类型是Map<String,ObjectFactory<?>>。接下来在属性注入的端,发现他依赖了另外一个bean,那么又会去getBean进行bean的创建和初始化,然后同样也在实例后初始化前把他封装为ObjectFactory对象,放入二级缓存,然后就进行属性注入。而他又回过头来依赖了原来的bean,所有又调用了getBean获取原来的bean,这是后getBean一进来,从二级缓存中取到了,就不会往下进行这个bean的实例化了,然后完成了先把他赋值个第二个bean的成员变量,然后原来的bean也完成属性注入和初始化回调。

具体步骤:

  1. A依赖了B,B又依赖A
  2. getBean(A),创建A
  3. 判断符合循环依赖条件,把A的ObjectFactory对象放入二级缓存,这个对象的getObject方法,会返回A
  4. 进行属性注入
  5. 发现依赖了B,getBean(B),进行B的创建
  6. B也走了上面1-5步
  7. B 依赖了 A,所以A又调用 getBean(A)
  8. 这是二级缓存中已经存了A的ObjectFactory,就调用ObjectFactory的getObject方法,返回A,不再继续往下进行实例化
  9. B 先完成属性注入和初始化回调
  10. A 也完成属性注入和初始化回调

尝试从缓存中获取的具体流程

在调用getBean方法创建或后取一个bean是,首先会去缓存中取,如果从三个缓存池的随便一个当中取到了,就不会往下进行实例化。
getBean或走到doGetBean方法,然后里面有一行代码,是关于从缓存中尝试获取bean的逻辑

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

/**
 * 尝试从BeanFactory的三个缓存池中获取该bean
 * 获取得到则返回,不走下面的创建初始化方法。
 * 此处作用:
 * 1.给已经创建过的bean,再调getBean是获取该bean
 * 2.处理循环依赖时返回该bean的半成品(已创建未初始化)
 */
Object sharedInstance = getSingleton(beanName);

已经完成初始化的bean,以及循环依赖时提前暴露的bean,在这里都可以获取到,不管是哪一种,只要获取到了,就不会往下进行重复的实例化工作。

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)

	@Override
	@Nullable
	public Object getSingleton(String beanName) {
		/* allowEarlyReference为true表示允许循环引用 */
		return getSingleton(beanName, true);
	}

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		/*如果从一级缓存取到则直接返回,一般出现在获取已经完成的bean,或者单向引用 */
		Object singletonObject = this.singletonObjects.get(beanName);
		/*获取不到且当前beanName对应的Bean正在创建中(实例化后初始化前)*/
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				/*先从三级缓存中取*/
				singletonObject = this.earlySingletonObjects.get(beanName);
				/*三级完成取不到 && 允许循环引用*/
				if (singletonObject == null && allowEarlyReference) {
					/*从二级缓存中获取当前Bean对应的ObjectFactory */
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						/*获取到了,则调用getObject,获取当前正在创建中的对象*/
						singletonObject = singletonFactory.getObject();
						/*添加到三级缓存*/
						this.earlySingletonObjects.put(beanName, singletonObject);
						/*从二级缓存中删除,下次就直接从二级缓存中取了*/
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

这里可以看到spring的三个缓存次,singletonObjects-一级缓存池,也就是俗称的单例缓存池,singletonFactories-二级缓存池,earlySingletonObjects-三级缓存池,他们的类型全都是map,key就是beanName

  1. 从一级缓存取singletonObjects到则直接返回
  2. 从一级缓存去不到,如果运行循环引用,就从三级缓存池earlySingletonObjects中取,取到了直接返回
  3. 从二级缓存取不到,就从二级缓存singletonFactories中取,如果去到了,就调用取出来的ObjectFactory对象singletonFactory 的getObject方法,返回实例化后初始化前的bean,并且放入三级缓存,从二级缓存中删除

这里为什么要有三级缓存呢,而且二级缓存取出来的不是Object,而是ObjectFactory?这里先把问题留着,我们可以往下看,在进行提前暴露处理时,看看放入singletonFactories的ObjectFactory对象里面getObject方法的具体实现,就会有答案。

是否循环依赖依赖的判断

如果从缓存总没取到bean,就会进入doCreateBean方法,进行bean的创建在bean创建完成以后,会有一步是否运行循环依赖的判断

		/**
		 * createBeanInstance(beanName, mbd, args)实例化后,初始化前
		 * 判断是否支持循环引用,是否单例 && 支持循环引用 && 正在创建中(beanName在singletonsCurrentlyInCreation中)
		 * this.allowCircularReferences默认为true
		 * 这里为了解决循环引用,先缓存一个工厂,存放实例化后初始化前的单例
		 * org.springframework.beans.factory.ObjectFactory<T>
		 *     T getObject()
		 */
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
  • bean是否单例模式,默认肯定是单例
  • 支持循环依赖,默认也是支持的
  • bean是否正在创建中,这个是在调用creatBean方法前,会先把他的beanName放入一个set的集合(singletonsCurrentlyInCreation)中,标记为正在创建

所以如无意外,肯定成立

条件成立成立举进入提前暴露的处理阶段

提前暴露处理

		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			/*在二级缓存中,存放当前bean对应的工厂*/
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

addSingletonFactory就是把当前正在创建的bean提前暴露的处理,key是beanName,value是ObjectFactory对象

	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		/**
		 * singletonObjects是BeanFactory中的三级缓存,Map<String, Object>
		 * 专门缓存ObjectFactory,也就是俗称的Bean半成品,
		 * 实际上是个工厂,调用getObject就会返回该创建中的bean
		 */
		synchronized (this.singletonObjects) {
			if (!this.singletonObjects.containsKey(beanName)) {
				/*把ObjectFactory保存到singletonFactories中 */
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

回到刚刚的地方,可以看到ObjectFactory的getObject方法,这里调用的是getEarlyBeanReference方法

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

那么到时候从二级缓存获取到该ObjectFactory对象后,调用getObject方法时,就会去调用getEarlyBeanReference方法,那么我们看看getEarlyBeanReference的具体操作

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		/*判断:不是一个合成的Bean && BeanFactory中存在InstantiationAwareBeanPostProcessors*/
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			/*遍历所有的SmartInstantiationAwareBeanPostProcessor,执行getEarlyBeanReference方法,返回一个有可能被修改后的bean */
			for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
				// 所以利用SmartInstantiationAwareBeanPostProcessor可以改变一下提前暴露的对象
				exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
		return exposedObject;
	}

可以看到,他不是单纯的返回该bean。在返回该bean前,会先进行bean后置处理器的处理。这里所有的SmartInstantiationAwareBeanPostProcessor接口的实现类的getEarlyBeanReference方法可以得到回调。我们随便挑一个他的实现了看看

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// 如果该类有advice则创建proxy.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			/* 创建代理对象 */
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			/* 返回代理对象 */
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

可以看到,这里返回的有可能是个代理对象,那么我么取到的就不是原来的bean,而是原来bean的代理对象了。

这也回答了刚刚那个问题:

这里为什么要有三级缓存呢,而且二级缓存取出来的不是Object,而是ObjectFactory?这里先把问题留着,我们可以往下看,在进行提前暴露处理时,看看放入singletonFactories的ObjectFactory对象里面getObject方法的具体实现,就会有答案。

二级缓存存的是个ObjectFactory,那是因为调用他的getObject方法,返回的不一定是原来的bean,spring会在里面进行一些bean后置处理器的回调工作,允许修改该bean,或返回一个代理对象。getObject后返回的对象,又存的三级缓存中,从二级缓存删除,是因为不再进行重复的回调,这里返回经过处理后的对象,以后就从三级缓存中取了,没有必要进行重复处理。

属性注入触发getBean的位置

在doCreatBean方法中,创建完该bean,提前暴露完后。就会进行属性注入
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

			// 这一步也是非常关键的,这一步负责属性装配,因为前面的实例只是实例化了,并没有设值,这里就是设值
			// 并且标注了@PostConstruct的方法,也会在此调用,属于初始化回调
			populateBean(beanName, mbd, instanceWrapper);

进入populateBean(beanName, mbd, instanceWrapper),首先声明,下面的方法有点长,因为涉及到不用类型的属性注入,不用从头到尾看,我们挑其中一个方法看就行了
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		if (bw == null) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				return;
			}
		}

		// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
		// 到这步的时候,bean 实例化完成(通过工厂方法或构造方法),但是还没开始属性设值,
		// InstantiationAwareBeanPostProcessor 的实现类可以在这里对 bean 进行状态修改
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				// 调用InstantiationAwareBeanPostProcessor实现类的postProcessAfterInstantiation方法
				if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
					// 如果返回 false,代表不需要进行后续的属性设值,也不需要再经过其他的 BeanPostProcessor 的处理
					return;
				}
			}
		}

		// bean 实例的所有属性都在这里了
		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		// 如果自动装配类型为1-byName 或 2-byType 则进入此分支 但这里是标签形式的,不是注解@Autowire
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// 1-AUTOWIRE_BY_NAME 通过名字找到所有属性值,如果是 bean 依赖,先初始化依赖的 bean。记录依赖关系
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// 2-AUTOWIRE_BY_TYPE 通过类型装配。复杂一些
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}

		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

		/* 通过BeanPostProcessor完成注解版的自动装配  */
		PropertyDescriptor[] filteredPds = null;
		if (hasInstAwareBpps) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
				if (pvsToUse == null) {
					if (filteredPds == null) {
						filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
					}
					// 这里有个非常有用的 BeanPostProcessor 进到这里: AutowiredAnnotationBeanPostProcessor
					// 对采用 @Autowired、@Value 注解的依赖进行设值,这里的内容也是非常丰富的
					pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						return;
					}
				}
				pvs = pvsToUse;
			}
		}
		if (needsDepCheck) {
			if (filteredPds == null) {
				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			}
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}

		if (pvs != null) {
			// 设置 bean 实例的属性值
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}

我们看autowireByName(beanName, mbd, bw, newPvs)方法,其他的可以忽略

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireByName

	protected void autowireByName(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			if (containsBean(propertyName)) {
				/**
				 * 属性注入,getBean获取依赖的Bean,有可能没有,需要创建
				 * 有可能已经创建完成,直接从一级缓存singleObjects中获取,
				 * 或者涉及到循环引用,从三级缓存SingletonFactoryes中获取
				 */
				Object bean = getBean(propertyName);
				pvs.add(propertyName, bean);
				/*注册一下依赖关系*/
				registerDependentBean(propertyName, beanName);
				if (logger.isTraceEnabled()) {
					...
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					...
				}
			}
		}
	}

可以看到里面有调用了getBean方法,获取或创建依赖的bean。

这时就会进行依赖的bean的创建,然后依赖的bean也走了相同的流程,在进入原来的bean的getBean方法,就可以从二级缓存中获取到该bean的ObjectFactory对象,然后调用getObject方法,获取到该bean的半成品。

  1. A依赖了B,B又依赖A
  2. getBean(A),创建A
  3. 判断符合循环依赖条件,把A的ObjectFactory对象放入二级缓存,这个对象的getObject方法,会返回A
  4. 进行属性注入
  5. 发现依赖了B,getBean(B),进行B的创建
  6. B也走了上面1-5步
  7. B 依赖了 A,所以A又调用 getBean(A)
  8. 这是二级缓存中已经存了A的ObjectFactory,就调用ObjectFactory的getObject方法,返回A,不再继续往下进行实例化
  9. B 先完成属性注入和初始化回调
  10. A 也完成属性注入和初始化回调

一般循环依赖分析到这里就结束了,但其实下面还有一步

循环引用完成后对bean的类型进行判断

在doCreateBean方法中差不多结尾处,有这么一段长长的方法

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

		/**
		 * 这里也涉及循环引用的问题:
		 * 此时该bean已经实例化完成,执行了BeanPostProcesser的两个回调方法,
		 * 有可能该Bean的类型已经改变,也就是说和他实例化时的类型不一致,
		 * 因为循环引用是在该bean在实例化后初始化前提前暴露,让别的bean引用而完成的
		 * 此时已经是该bean初始化完成,如果类型不一致,又检测到有其他已经创建完成的bean依赖了这个bean,就会报错
		 */
		if (earlySingletonExposure) {
			// 尝试从缓存中获取单例,注意后面的参数为false,表示不从第三级缓存singletonFactories中获取,为什么呢?因为这里不允许循环依赖
			Object earlySingletonReference = getSingleton(beanName, false);
			//如果不为null,就会进入if条件中,因为earlySingletonReference不为null,说明存在循环引用,
			//为什么呢?因为第一个处理的时候,会将引用放到singletonFactories缓存中,当循环依赖注入的时候,
			//会通过singletonFactories中拿到提前暴露的引用,然后放到第二级缓存earlySingletonObjects中。
			//所以,在这里拿到了earlySingletonReference,表明存在循环引用。
			if (earlySingletonReference != null) {
				//如果相等,那么就什么也不做,将earlySingletonReference返回回去即可
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				/**
				 * 下面就是类型不相等的处理
				 * 类型不相等 && 已经有创建完成的bean依赖此Bean
				 * 满足这两个条件,就报错
				 */
				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.");
					}
				}
			}
		}

因为在循环引用中,先完成初始化的bean引用到的bean是未完成初始化的bean,而初始化回调有可能修改了该bean的类型,但是先完成初始化的bean因为已经完成了实例化和初始化的整个工作,可以认为已经投入使用中了,这时是不允许他的引用的对象类型发生改变的,否则先完成初始化的bean所引用的bean的类型,跟单例缓存池中的存放的类型就不一致了,也就是不是同一个对象了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值