Spring源码解析(7)之循环依赖解决

一、前言

        在之前的博客中已经介绍到了Spring的bean的实例化过程,但是介绍到doCreateBean并没有详细去介绍,这里会接着去介绍doCreateBean具体做了什么事情,大家如果没有阅读过之前的博客,可以先去了解一下bean的加载过程,在我之前的博客都有具体的介绍。Spring源码解析(5)之bean实例化过程(上)_jokeMqc的博客-CSDN博客

        下面是Spring解决循环依赖的草图,大家要理解这张图才能够对Spring是如何解决循环依赖以及有哪些循环依赖是Spring无法解决的。

二、什么是循环依赖

        循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方,比如A引用B,B引用A。

     2.1Spring如何解决循环依赖

        Spring容器循环依赖包括构造器循环依赖和setter循环依赖。Spring依赖主要分为一下几种情况。

2.1.1 构造器循环依赖

        表示通过构造器注入构成的循环依赖,次依赖是无法解决的,只能抛出异常。为什么呢?在前面的源码解析中已经看到了,Spring容器将每一个正在创建的单例的放在一个"正在创建的bean"的缓存池里面,如果该bean正在创建过程中,这个缓存一直存在。

        

  <bean id="instanceA"  class="com.tuling.circulardependencies.InstanceA" >
        <constructor-arg name="instanceB"  ref="instanceB"></constructor-arg>
    </bean>

    <bean id="instanceB" class="com.tuling.circulardependencies.InstanceB" >
        <constructor-arg name="instanceA" ref="instanceA"></constructor-arg>
    </bean>

        针对以上的代码的分析如下:

  1. Spring容器创建"InstanceA"bean,首先去"当前创建的bean的缓存池"查找当前的bean是否正在创建,如果没有发现,则创建该bean,然后把当前的bean放到"当前创建的bean的缓存池"中然后准备其需要的构造参数"InstanceB"。
  2. Spring创建"InstanceB",首先去"当前创建的bean的缓存池"查找当前的bean是否正在创建,如果没有发现,则创建该bean,然后把当前的bean放到"当前创建的bean的缓存池"中然后准备其需要的构造参数"InstanceA"。
  3. 此时Spring容器需要去创建"InstanceA",就会发现该bean正在创建,因为表示循环依赖,就会抛出异常。

2.1.2 sertter循环依赖 

        表示通过setter注入方式构成的循环依赖,对于setter注入造成的循环依赖是通过Spring容器提前暴露刚完成但未初始化的bean来完成的,而且只能够解决单例作用域的bean,因为prototype并不会提前放入到缓存中。

		// 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.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}

         具体步骤如下:

  1. Spring容器创建单例"InstanceA"bean,首先根据午餐构造器创建bean,会判断当前bean, “是否单例&是否允许循环依赖&是否正在创建bean”,如果满足会提前将该早期对象(ObjectFactory)提前暴露到三级缓存中,然后进行属性赋值,setter注入"InstanceB"。
  2. Spring容器创建单例"InstanceB",首先根据无参构造器创建bean,然后会将该bean提前暴露到三级缓存中,然后setter注入"IntanceA".
  3. 进行注入"InstanceA"由于提前暴露了ObjectFactory工厂,从而使用它返回提前暴露一个创建中的bean。

2.1.3 prototype范围的依赖处理

        对于"prototype"作用域bean,Spring容器无法完成依赖注入,因为Spring容器不进行缓存"prototype"作用域的bean,因此无法提前暴露一个创建中的bean。

猜你喜欢

转载自blog.csdn.net/jokeMqc/article/details/120832539