文章目录
- 1.spring 解决循环依赖的步骤
- 2.源码分析
先说一下循环依赖:
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类图:
通过类图可发现,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方法赋值。