Spring 源码(八)循环依赖

循环依赖是指两个或者多个Bean以前相互持有对方。在Spring中循环依赖通常有三种方式:java

  1. 构造函数循环依赖
  2. setter方法循环依赖
  3. prototype 范围的依赖处理

构造函数循环依赖

在Spring中构造函数循环依赖是没法解决的,由于构造函数依赖实际上是方法间循环调用的一种,会发生死循环。可是在Spring中会直接抛出BeanCurrentlyInCreationException异常。源码以下:git

// 在缓存中获取Bean,若是没有就建立Bean
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(beanName, "'beanName' must not be null");
	synchronized (this.singletonObjects) {
		// 在缓存中获取Bean
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
			// 判断容器是否正在销毁单实例Bean
			if (this.singletonsCurrentlyInDestruction) {
				throw new BeanCreationNotAllowedException(beanName,
						"Singleton bean creation not allowed while singletons of this factory are in destruction " +
						"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
			}
			// 将当前须要建立的Bean标示放到Set集合,若是失败则抛出BeanCurrentlyInCreationException异常
			beforeSingletonCreation(beanName);
			boolean newSingleton = false;
			boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
			if (recordSuppressedExceptions) {
				this.suppressedExceptions = new LinkedHashSet<Exception>();
			}
			try {
				// 建立Bean实例
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			}
			...
			if (newSingleton) {
				// 将Bean实例注册到singletonObjects容器中
				addSingleton(beanName, singletonObject);
			}
		}
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}
}

protected void beforeSingletonCreation(String beanName) {
	// 将当前须要建立的Bean标示方法Set集合,若是失败则抛出BeanCurrentlyInCreationException异常
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}
}

执行过程:github

  1. 从缓存中获取Bean,若是没有则走建立Bean流程
  2. 判断容器是否正在销毁单实例Bean,若是是则不建立Bean
  3. 将当前须要建立的Bean标示(name)放入Set集合中(当前正在建立的Bean池),若是放入失败则抛出BeanCurrentlyInCreationException异常
  4. 建立Bean实例
  5. 将Bean实例注册到容器(放到缓存中)

解决构造函数依赖主要是第3步实现的,Spring在容器建立的Bean的时候,会将Bean的标示(name)放到一个Set集合里面(当前正在建立的Bean池)。当在建立Bean的过程当中,发现自已经在这个Set集合中时,就直接会抛出BeanCurrentlyInCreationException异常,而不会发生死循环。spring

setter方法循环依赖

@Service
public class AService {
    @Autowired
    private BService bService;

    @Autowired
    public void setbService(BService bService) {
        this.bService = bService;
    }
}

这两种方式都算是setter方法依赖。咱们建立单实例Bean的大体过程能够划分红三个阶段:缓存

  1. 实例化 createBeanInstance(beanName, mbd, args);
  2. 设置属性值 populateBean(beanName, mbd, instanceWrapper);
  3. 初始化 initializeBean(beanName, exposedObject, mbd);

对于Setter注入形成的循环依赖,Spring容器是在建立Bean第一步实例化后,就将Bean的引用提早暴露出来。经过提早暴露出一个单例工厂方法,从而使得其余Bean能够引用到该Bean。app

建立Bean时提早暴露刚完成第一步的Bean,源码以下:ide

addSingletonFactory(beanName, new ObjectFactory<Object>() {
	@Override
	public Object getObject() throws BeansException {
		return getEarlyBeanReference(beanName, mbd, 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);
		}
	}
}

自动装配过程当中获取单实例Bean,源码以下:函数

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					singletonObject = singletonFactory.getObject();
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

咱们能够看到,在自动装配Bean的过程当中,会去找三个缓存:spring-boot

  1. singletonObjects:存放完成建立的Bean全部步骤的单实例Bean
  2. earlySingletonObjects:存放只完成了建立Bean的第一步,且是由单实例工厂建立的Bean
  3. singletonFactories:存放只完成了建立Bean的第一步后,提早暴露Bean的单实例工厂。

这里为何会用一个三级缓存呢,为啥不直接将只完成了建立Bean的第一步的Bean直接方到earlySingletonObjects缓存中呢?this

咱们跟入 getEarlyBeanReference(beanName, mbd, bean)咱们能够发现,单实例工厂方法返回Bean的时候还执行了后置处理器的,在后置处理器中咱们还能够对Bean进行一些特殊处理。若是咱们直接将刚完成实例化的Bean放入earlySingletonObjects缓存中,那么失去对Bean进行特殊处理的机会。

源码以下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				if (exposedObject == null) {
					return null;
				}
			}
		}
	}
	return exposedObject;
}

对于singleton 做用域 bean ,能够经过setAllowCircularReferences(false);来禁用循环引用。

prototype 范围的依赖处理

对于prototype 做用域 bean, Spring 容器没法完成依赖注入,由于 Spring 容器不进行缓存prototype做用域的 bean ,所以没法提早暴露一个建立中的 bean 示。

源码

https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases

spring-boot-student-spring 工程

参考

《Spring源码深度解析》

相关文章
相关标签/搜索