spring5源码阅读(四)解决循环依赖很简单

文章目录


先说一下循环依赖:
1.A依赖B;同时B依赖A;
2.A依赖B,B依赖C,C依赖A;
总之能形成B环,就是存在循环依赖。

上篇文章中spring5源码阅读(三),我们解释了一种循环依赖,就是使用@DependsOn注解修饰的,spring如果发现有这种循环依赖,就会直接抛异常。

还有一种类型的循环依赖,就是通过字段属性依赖,比如:

public class A {
	@Resource
	private B b;
}

public class B {
	@Resource
	private A a;
}

或者A依赖B,B依赖C,C依赖A,也是一样的道理。这种通过属性的循环依赖,并且是单例类型(非单例的spring也不支持循环依赖),spring默认是允许的。

1.spring 解决循环依赖的步骤

先整体描述下spring解决循环依赖的步骤,咱们就拿A依赖B,B依赖A这种最简单的说明:

1. 假设spring先扫描到了A,实例化了A之后,开始初始化A的属性,此时发现A依赖了B,那么就需要去先实例化B;

2. spring开始实例化B,但是实例化B之后,并不会着急初始化B,也就是不去初始化B的属性;并把B放到一个map中缓存起来,此时B的状态,spring称作earlyBean,就是早期的bean;

3. 完成A的初始化;

4. spring继续完成B的初始化,自初始化B的属性A时,因为此时容器中A已经存在,所以不用循环实例化A;此时直接完成B的初始化;

5. 第3不步结束后,实并没有真正完成A的初始化,因为A的属性b虽然不是空了,但是B的属性a还没有赋值,经过步骤4之后,其实A和B才真正完全初始化。

其实光说循环依赖,步骤就是上边这么简单。

2.源码分析

通过上述步骤,最开始假设spring先扫描到了A(先谁都一样),那么就开始实例化A。实例化的流程,就是上篇文章中的内容;我们直接从上篇文章的结尾,AbstractAutowireCapableBeanFactory#doCreateBean()方法开始。

2.1 addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)

doCreateBean中实例化bean,初始化之前,有这么一段:

//省略。。。
    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");
		}
		//缓存到map
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
	//省略。。。

这段就是未雨绸缪,不管有没有循环依赖,都缓存当前bean和器对应的ObjectFactory。

earlySingletonExposure的值这里是true,表示允许提前曝光bean;这里的early,表示早期的,为啥说早期呢,因为此时bean刚被实例化,但是还没有被初始化,也就是bean中的属性还没有被赋值。

此时的bean的状态,也就是第二个参数ObjectFactory<?> singletonFactory,至于getEarlyBeanReference的细节不展开,只要记得它表示bean的早期状态就行。

看下addSingletonFactory如何缓存的:

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

this.singletonObjects:单例池,就是所有实例化并初始化好的bean,都回放在这里缓存;此时正在实例化A,这个单例池中肯定么有,进入if;
this.singletonFactories:ObjectFactory就是缓存在这里;
this.earlySingletonObjects:早期的bean对象,就是实例化了但是还没初始化的bean都在这里缓存。因为A没往里放过,此时这里肯定remove了个null;

其实看到这里,循环依赖的解决办法,我们基本已经清楚了,关键就是spring弄了个earlySingletonObjects;
也就是说,此时A中的属性b,在实实例化的时候,也存在这么一个状态;实例化b后,并没有着急去初始化它,直接将这种早期状态的b注入给了a;
因为b没有着急去初始化化,自然也就不存在实例化它所依赖的a的过程,也就不存在循环依赖了。

至于B何时被实例化,何时将它的早期状态缓存起来,又是如何注入给A的,继续往下看。

2.2 populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)

在类A中,因为参数 private B b 使用了@Resource(或者@Autowried)注解修饰,因此会被初始化并注入值。
此方法的作用是填充bean的属性值,也就是初始化A的属性值,这里是参数b。

因为要初始化参数b,就必然要得到参数b的实例,必然就涉及到b的实例化;但是实例化b的时候,又依赖参数a,这就造成了循环依赖,继续往下看如何解决。

此方法主要流程就是遍历属性,使用不同的后置处理器去注入参数。

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

	//bean实例化之后,但是属性还没有被set之前调用InstantiationAwareBeanPostProcessor
	// 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.
	boolean continueWithPropertyPopulation = true;

	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
					//这里一般进不来,进来了就是不允许初始化bean的属性
					continueWithPropertyPopulation = false;
					break;
				}
			}
		}
	}

	if (!continueWithPropertyPopulation) {
		return;
	}

	PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

	//注入方式:按照名称或者类型;这里都不是,默认值是0
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
		MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
		// Add property values based on autowire by name if applicable.
		if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
			autowireByName(beanName, mbd, bw, newPvs);
		}
		// Add property values based on autowire by type if applicable.
		if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
			autowireByType(beanName, mbd, bw, newPvs);
		}
		pvs = newPvs;
	}

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

	if (hasInstAwareBpps || needsDepCheck) {
		if (pvs == null) {
			pvs = mbd.getPropertyValues();
		}
		PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
		if (hasInstAwareBpps) {
		    //重点在这里
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				//CommonAnnotationBeanPostProcessor
				//AutowiredAnnotationBeanPostProcessor等
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
					if (pvs == null) {
						return;
					}
				}
			}
		}
		if (needsDepCheck) {
			//遍历检查属性是否都被set值了
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}
	}

	if (pvs != null) {
		applyPropertyValues(beanName, mbd, bw, pvs);
	}
}

整个方法比较重要的是这个for循环,

for (BeanPostProcessor bp : getBeanPostProcessors()) {
	//CommonAnnotationBeanPostProcessor
	//AutowiredAnnotationBeanPostProcessor等
	if (bp instanceof InstantiationAwareBeanPostProcessor) {
		InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
		pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
		if (pvs == null) {
			return;
		}
	}
}

这里遍历得到InstantiationAwareBeanPostProcessor类型的后置处理器,执行回调方法postProcessPropertyValues。

比如CommonAnnotationBeanPostProcessor.java,用于发现并缓存@Resource等注解修饰的属性;
比如AutowiredAnnotationBeanPostProcessor.java,用于发现并缓存@Autowired/@Value等注解修饰的属性;

因为我们使用了@Resource注解,因此我们继续看CommonAnnotationBeanPostProcessor的回调方法postProcessPropertyValues,@Autowired等注解修饰的,流程基本一致。

在看CommonAnnotationBeanPostProcessor#postProcessPropertyValues回调方法之前,有必要先说一下它的另外一个回调方法postProcessMergedBeanDefinition()。

2.3 CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition()

为什么先说这个方法呢,因为这个回调方法是先被执行的。

具体位置是,在上篇文章中spring5源码阅读(三)最后一节doCreateBean方法中(populateBean方法之前),有如一下一行代码:
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

打开,

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
	if (bp instanceof MergedBeanDefinitionPostProcessor) {
		MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
		bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
	}
}

这段方法是在遍历执行MergedBeanDefinitionPostProcessor类型的回调方法,也会找到CommonAnnotationBeanPostProcessor,到这感觉有点乱,我们看看CommonAnnotationBeanPostProcessor类图:
spring5源码阅读(四)解决循环依赖很简单
通过类图可发现,CommonAnnotationBeanPostProcessor 既实现了MergedBeanDefinitionPostProcessor接口,又实现了InstantiationAwareBeanPostProcessor接口。

看下具体的回调方法内容:

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
	super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
	InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
	metadata.checkConfigMembers(beanDefinition);
}

记住这个findResourceMetadata(beanName, beanType, null)方法,因为下边还会执行一次,我们后边在分析。

2.4 CommonAnnotationBeanPostProcessor#postProcessPropertyValues()

上边2.3节说完了CommonAnnotationBeanPostProcessor的第一个先执行的回调方法,这继续说第二个,
进入 CommonAnnotationBeanPostProcessor的回调方法postProcessPropertyValues:

public PropertyValues postProcessPropertyValues(
		PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
    //InjectionMetadata就是类中注入的属性等信息
	InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
	try {
		metadata.inject(bean, beanName, pvs);
	} catch (Throwable ex) {
		throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
	}
	return pvs;
}

此方法分为两步:

  • 利用findResourceMetadata方法,得到类中@Resource注解修饰的属性和方法,返回InjectionMetadata类型的对象;
  • 利用metadata.inject方法,对这些属性,进行值的注入,也就是赋值

findResourceMetadata()就是在2.3节中已经执行过一次的方法。

下面我们分开说。

2.4.1 findResourceMetadata() 方法

此方法的作用是拿到类中的属性和方法,当然不是所有的,是目的性的获取,比如被@Resource注解修饰的。

看代码:

private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {
	// Fall back to class name as cache key, for backwards compatibility with custom callers.
	String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
	// Quick check on the concurrent map first, with minimal locking.
	//InjectionMetadata就是类中的属性等信息
	InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
	if (InjectionMetadata.needsRefresh(metadata, clazz)) {
		synchronized (this.injectionMetadataCache) {
			metadata = this.injectionMetadataCache.get(cacheKey);
			if (InjectionMetadata.needsRefresh(metadata, clazz)) {
				if (metadata != null) {
					metadata.clear(pvs);
				}
				//利用反射遍历类中的属性和方法,找到被@Resource等注解修饰的属性或者方法
				metadata = buildResourceMetadata(clazz);
				this.injectionMetadataCache.put(cacheKey, metadata);
			}
		}
	}
	return metadata;
}

2.3节我们说了,此方法其实在前边已经被执行过。
第一次执行的时候,metadata肯定是null,也即是缓存中没有,会进入if判断中。

简单看下if判断,返回true。

public static boolean needsRefresh(@Nullable InjectionMetadata metadata, Class<?> clazz) {
	return (metadata == null || metadata.targetClass != clazz);
}

最后会执行metadata = buildResourceMetadata(clazz)方法,此方法我们就不展开了,作用就是利用反射遍历类中的属性和方法,找到被@Resource等注解修饰的属性或者方法。

第二次执行的时候,就直接从缓存中取就行了。

2.4.2 InjectionMetadata#inject(bean, beanName, pvs)

拿到了类的属性和方法之后,这里通过inject方法进行注入操作,就是给属性赋值。
比如这里我们正在初始化类A,其中有个参数 @Resource private B b; 那么就需要把B的实例,赋值给参数b。

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	Collection<InjectedElement> checkedElements = this.checkedElements;
	Collection<InjectedElement> elementsToIterate =
			(checkedElements != null ? checkedElements : this.injectedElements);
	if (!elementsToIterate.isEmpty()) {
		for (InjectedElement element : elementsToIterate) {
			if (logger.isDebugEnabled()) {
				logger.debug("Processing injected element of bean '" + beanName + "': " + element);
			}
			element.inject(target, beanName, pvs);
		}
	}
}

整体思路就是遍历属性,比如我们这里只有属性b;然后执行属性的inject方法。

inject方法我们就不继续了,大致思路就是:

  • 调用了bean工厂的getBean方法,也就是上一篇文章的全部内容,最终必然会通过getEarlyBeanReference得到早期的实例a,此时的a没有被初始化;
  • 通过反射,调用属性的set方法赋值。
上一篇:spring源码分析——bean的实例化流程


下一篇:Spring源码之Spring后置处理器详解