Spring源码-Spring为什么使用三级缓存解决循环依赖

Spring使用三级缓存,分别是singletonObjects,earlySingletonObjects,singletonFactories来解决循环依赖问题。但是用二级缓存就可以解决循环依赖了。为什么要使用三级缓存呢?因为有动态代理。必须保证单例bean在bean工厂中只有一个对象。所以提供了singletonFactories用lamada表达式用来将代理后的对象替换被代理对象。保证有动态代理bean工厂也只有一个单例bean。

一、修改为二级缓存

修改DefaultSingletonBeanRegistry.getSingleton(String beanName, boolean allowEarlyReference),注释原来的这个函数,复制一份改为:

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

在DefaultSingletonBeanRegistry类新增以下方法:

protected void addearlySingletonObjects(String beanName, Object object) {
	Assert.notNull(object, "Singleton bean must not be null");
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			this.earlySingletonObjects.put(beanName,object);
			this.registeredSingletons.add(beanName);
		}
	}
}

注释AbstractAutowireCapableBeanFactory.doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法里面的addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));这行代码,在下面新增:

addearlySingletonObjects(beanName,bean);

在没有动态代理时可验证可解决循环依赖。

现在加上动态代理:

Logger.java

public class Logger {

public void dothing() {
	System.out.println("现在时间是:"+ DateFormat.getDateInstance().format(new Date()));
}
}

circle.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:mvc="http://www.springframework.org/schema/mvc"
	   xmlns:p="http://www.springframework.org/schema/p"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">


	<bean id="a" class="circle.A">
		<property name="b" ref="b"/>
	</bean>


	<bean id="b" class="circle.B">
		<property name="a" ref="a"/>
	</bean>

	<bean id="logger" class="circle.Logger"/>

	<aop:config>
		<aop:aspect id="logger" ref="logger">
			<aop:pointcut expression="execution(* circle.*.*(..))" id="method" />
			<aop:before method="dothing" pointcut-ref="method" />
		</aop:aspect>
	</aop:config>
</beans>

调试可知:

新建a,调用AbstractAutowireCapableBeanFactory.doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)调用createBeanInstance(beanName, mbd, args)实例化a,调用populateBean(beanName, mbd, instanceWrapper)填充a的属性b,populateBean调用applyPropertyValues(beanName, mbd, bw, pvs)填充属性,applyPropertyValues又调用valueResolver.resolveValueIfNecessary(pv, originalValue)解析获取b,resolveValueIfNecessary通过resolveReference,resolveReference通过beanFactory.getBean(resolvedName)获取b,经过b的实例化和从earlySingletonObjects中获取属性a完成了b的创建和属性填充。在调用populateBean后,调用initializeBean(beanName, exposedObject, mbd)生成了b的代理对象并在DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory<?> singletonFactory)通过addSingleton(String beanName, Object singletonObject)将b放入singletonObjects
。返回填充a的属性b后:

通过initializeBean(beanName, exposedObject, mbd)生成a的代理,而earlySingletonObjects保存的a是A@2397,即bean,生成a的代理对象是circle.A@769a1df5,即exposedObject,AbstractAutowireCapableBeanFactory.doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)调用

	if (earlySingletonExposure) {
		Object earlySingletonReference = getSingleton(beanName, false);
		if (earlySingletonReference != null) {
			if (exposedObject == bean) {
				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.");
				}
			}
		}
	}

导致exposedObject == bean为false,有两个不同的bean而抛出无法解决循环依赖的异常。

Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Bean with name 'a' has been injected into other beans [b] 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.

posted @ 2022-10-05 21:59  shigp1  阅读(424)  评论(0)    收藏  举报