Spring的循环依赖

一、什么是循环依赖

        循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方,比如TestA引用TestB,TestB又引用TestA,则它们最终反映为一个环。如图

二、setter循环依赖

2.1 总体流程图

2.2 源码分析

配置文件配置

<bean id="testA" class="com.xyx.circular.TestA ">
		<property name="testB" ref="testB"/>
	</bean>
	<bean id="testB" class="com.xyx.circular.TestB">
		<property name="testA" ref="testA"/>
	</bean>

调用doGetBean,name=testA

protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
        //获取beanName
		String beanName = transformedBeanName(name);
		Object beanInstance;

		// Eagerly check singleton cache for manually registered singletons.
		//获取原始状态的bean
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			......
		}

		else {
			

			try {
				
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				......

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

				else if (mbd.isPrototype()) {......}

				else {......}
			}
			catch (BeansException ex) {
				beanCreation.tag("exception", ex.getClass().toString());
				beanCreation.tag("message", String.valueOf(ex.getMessage()));
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
			finally {
				beanCreation.end();
			}
		}
		return adaptBeanInstance(name, beanInstance, requiredType);
	}

调用getSingleton(beanName)从缓存获取,没有取到,并且此时testA不是在创建中,故而返回null

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Spring首先从singletonObjects(一级缓存)中尝试获取
		Object singletonObject = this.singletonObjects.get(beanName);
		// 若是获取不到而且对象在建立中,则尝试从earlySingletonObjects(二级缓存)中获取
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			......
		}
		return singletonObject;
	}

 调用getSingleton(String beanName, ObjectFactory<?> singletonFactory),将bean的beanName添加到isSingletonCurrentlyInCreation,来标志这个bean在创建中

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			//从一级缓存获取
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				......
				//在这里将bean的beanName添加到isSingletonCurrentlyInCreation
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				......
				try {
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
				      ......
				}
				catch (BeanCreationException ex) {
				             ......
				}
				finally {
					......
				}
				.......
			}
			return singletonObject;
		}

调用ObjectFactory.getObject()是个函数式接口,实质调用createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args),createBean委托给doCreateBean完成bean的创建

	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		......
		if (instanceWrapper == null) {
			//根据指定bean使用对应的策略创建新的实例,如:工厂方法、构造函数自动注入、简单初始化
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		......

		// 是否需要提早曝光:单例&允许循环依赖&当前bean正在创建中,检测循环依赖
		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");
			}
			//避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
			//AOP就是在这里将advice动态织入bean中
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//对bean进行填充,将各个属性值注入
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			......
		}

                                ......
		return exposedObject;
	}

 根据指定bean使用对应的策略创建新的实例,调用addSingletonFactory并将半成品的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);
			}
		}
	}

 调用populateBean完成属性赋值,因为testA依赖于testB,所以调用beanFactory.getBean(name)来获取bean,getBean又会委托给doGetBean,回到了分析的起点,只不过testB在完成属性赋值时,getSingleton(beanName)从缓存获取,此时可以从三级缓存获取到ObjectFactory的实例,在调用getObject来获取testA的实例,然后从三级缓存删除,放入二级缓存testA的实例

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Spring首先从singletonObjects(一级缓存)中尝试获取
		Object singletonObject = this.singletonObjects.get(beanName);
		// 若是获取不到而且对象在建立中,则尝试从earlySingletonObjects(二级缓存)中获取
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			//如果此bean正在加载则不处理
			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;
	}

 此时testB完成属性赋值,已经是一个完整的bean,然后放入一级缓存,并清除二级和三级缓存

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			//从一级缓存获取
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				 ......
				//在这里将bean的beanName添加到isSingletonCurrentlyInCreation
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				//调用createBean
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
				....
				if (newSingleton) {//放到一级缓存
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}


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

 之后又回到对testA的赋值,此时已经拿到了testB的实例,testA完成属性赋值,也放入一级缓存中。

三、其他循环依赖

3.1 构造循环依赖

public class TestA {
	private TestB testB;
	
	public TestA(TestB testB) {
		this.testB = testB;
	}

	public TestB getTestB() {
		return testB;
	}
	
	public void setTestB(TestB testB) {
		this.testB = testB;
	}
}

public class TestB {
  private TestA testA;

	public  TestB(TestA testA){
	  this.testA = testA;
  }

	public TestA getTestA() {
		return testA;
	}

	public void setTestA(TestA testA) {
		this.testA = testA;
	}
}

对于构造函数依赖,spring是没有解决的,因为根本没有走到放进三级缓存那一步。如何解决呢?其实就是构造时给一个中间对象即可。有两种方式:采用代理或者工厂

采用代理的方式:

(1)使用@Lazy注解

(2)使用@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)

推荐使用第一种,因为第二种方式会增加多余的BeanDefinition

采用工厂的

使用ObjectFactory,例如在TestA声明一个ObjectFactory<TestB> ,需要testB直接调用ObjectFactory.getObject()

3.2 prototype范围的循环依赖

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

<bean id="testA" class="com.xyx.circular.TestA" scope="prototype">
		<property name="testB" ref="testB"/>
	</bean>
	<bean id="testB" class="com.xyx.circular.TestB" scope="prototype">
		<property name="testA" ref="testA"/>
	</bean>

 3.3 depends-on循环依赖

<bean id="testA" class="com.xyx.circular.TestA" depends-on="testB">
		
	</bean>
	<bean id="testB" class="com.xyx.circular.TestB" depends-on="testA">
		
	</bean>

这种方式可以通过调整DependsOn依赖关系来解决

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值