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);

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

已经完成初始化的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);
	}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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;
	}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

这里可以看到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));

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 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));
		}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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);
			}
		}
	}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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

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

   
   
  • 1

那么到时候从二级缓存获取到该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;
	}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

可以看到,他不是单纯的返回该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);
	}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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;
		}
	<span class="token comment">// 如果该类有advice则创建proxy.</span>
	Object<span class="token punctuation">[</span><span class="token punctuation">]</span> specificInterceptors <span class="token operator">=</span> <span class="token function">getAdvicesAndAdvisorsForBean</span><span class="token punctuation">(</span>bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>specificInterceptors <span class="token operator">!=</span> DO_NOT_PROXY<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token keyword">this</span><span class="token punctuation">.</span>advisedBeans<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> Boolean<span class="token punctuation">.</span>TRUE<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token comment">/* 创建代理对象 */</span>
		Object proxy <span class="token operator">=</span> <span class="token function">createProxy</span><span class="token punctuation">(</span>
				bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> specificInterceptors<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">SingletonTargetSource</span><span class="token punctuation">(</span>bean<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">this</span><span class="token punctuation">.</span>proxyTypes<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> proxy<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token comment">/* 返回代理对象 */</span>
		<span class="token keyword">return</span> proxy<span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

	<span class="token keyword">this</span><span class="token punctuation">.</span>advisedBeans<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> Boolean<span class="token punctuation">.</span>FALSE<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">return</span> bean<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

可以看到,这里返回的有可能是个代理对象,那么我么取到的就不是原来的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);

 
 
  • 1
  • 2
  • 3

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

	<span class="token comment">// bean 实例的所有属性都在这里了</span>
	PropertyValues pvs <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">hasPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">?</span> mbd<span class="token punctuation">.</span><span class="token function">getPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>

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

	<span class="token keyword">boolean</span> hasInstAwareBpps <span class="token operator">=</span> <span class="token function">hasInstantiationAwareBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">boolean</span> needsDepCheck <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getDependencyCheck</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> AbstractBeanDefinition<span class="token punctuation">.</span>DEPENDENCY_CHECK_NONE<span class="token punctuation">)</span><span class="token punctuation">;</span>

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

	<span class="token keyword">if</span> <span class="token punctuation">(</span>pvs <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token comment">// 设置 bean 实例的属性值</span>
		<span class="token function">applyPropertyValues</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bw<span class="token punctuation">,</span> pvs<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

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

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

	protected void autowireByName(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
	String<span class="token punctuation">[</span><span class="token punctuation">]</span> propertyNames <span class="token operator">=</span> <span class="token function">unsatisfiedNonSimpleProperties</span><span class="token punctuation">(</span>mbd<span class="token punctuation">,</span> bw<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">for</span> <span class="token punctuation">(</span>String propertyName <span class="token operator">:</span> propertyNames<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">containsBean</span><span class="token punctuation">(</span>propertyName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token comment">/**
			 * 属性注入,getBean获取依赖的Bean,有可能没有,需要创建
			 * 有可能已经创建完成,直接从一级缓存singleObjects中获取,
			 * 或者涉及到循环引用,从三级缓存SingletonFactoryes中获取
			 */</span>
			Object bean <span class="token operator">=</span> <span class="token function">getBean</span><span class="token punctuation">(</span>propertyName<span class="token punctuation">)</span><span class="token punctuation">;</span>
			pvs<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>propertyName<span class="token punctuation">,</span> bean<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token comment">/*注册一下依赖关系*/</span>
			<span class="token function">registerDependentBean</span><span class="token punctuation">(</span>propertyName<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
			<span class="token punctuation">}</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
				<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
			<span class="token punctuation">}</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

可以看到里面有调用了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.");
					}
				}
			}
		}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值