所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。那么spring在自动注入的时候是如何解决这个问题的呢?
答案是:三级缓存,就是三个map,通过提早的暴露对象来解决这个问题。
三级缓存描述
DefaultSingletonBeanRegistry中的源码
//一级缓存:单例对象缓存池,beanName->Bean,其中存储的就是实例化,属性赋值成功之后的单例对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//三级缓存:单例工厂的缓存,beanName->ObjectFactory,添加进去的时候实例还未具备属性
// 用于保存beanName和创建bean的工厂之间的关系map,单例Bean在创建之初过早的暴露出去的Factory,
// 为什么采用工厂方式,是因为有些Bean是需要被代理的,总不能把代理前的暴露出去那就毫无意义了
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
//二级缓存:早期的单例对象,beanName->Bean,其中存储的是实例化之后,属性未赋值的单例对象
// 执行了工厂方法生产出来的Bean,bean被放进去之后,
// 那么当bean在创建过程中,就可以通过getBean方法获取到
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
//三级缓存是用来解决循环依赖,而这个缓存就是用来检测是否存在循环依赖的
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
调用链
下面是一个循环依赖的调用链。A->B->A的依赖注入
- 1.A为正在创建中,反射创建其实例,其对象工厂放入第三层缓存
- 2.初始化A实例时发现需要依赖注入B,则获取B的实例
- 3.标记B为正在创建中,反射创建其实例,其对象工厂放入第三层缓存
- 4.初始化B实例时发现需要依赖注入A,则获取A的实例
- 5.注意!这时候从缓存中获取时,A为正在创建中且第三层缓存有A的值了,所以调用缓存的对象工厂的getObject方法,把返回的A实例放入第二层缓存,删除第三层缓存
- 6.B实例初始化完成,放入第一层缓存,移除第二、三层中的缓存
- 7.回到第2步,A实例初始化完成,放入第一层缓存,移除第二、三层中的缓存
第一步肯定是要去获取Bean执行getBean(A),getBean->doGetBean
doGetBean中有两个getSingleton方法
getSingleton
从doGetBean方法的源码中依次说明
第一个是从缓存中获取,也就是图片最右边的,由于一开始缓存里面肯定没有所以返回的是null
// 尝试从单例缓存集合里获取bean实例,也就是图片中的getSingleton(2)
Object sharedInstance = getSingleton(beanName);
该方法在什么时候会有值呢?如上图最右边依次走到该流程的方法中,这个时候就可以获取到值。首次调用的时候是没有值的,因为还没有放进去。
来看下实现代码
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//尝试从一级缓存里面获取完备的Bean
Object singletonObject = this.singletonObjects.get(beanName);
//如果完备的单例还没有创建出来,创建中的Bean的名字会被保存在singletonsCurrentlyInCreation中
//因此看看是否正在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//尝试给一级缓存对象加锁,因为接下来就要对缓存对象操作了
synchronized (this.singletonObjects) {
//尝试从二级缓存earlySingletonObjects这个存储还没进行属性添加操作的Bean实例缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
//如果还没有获取到并且第二个参数为true,为true则表示bean允许被循环引用
if (singletonObject == null && allowEarlyReference) {
//从三级缓存singletonFactories这个ObjectFactory实例的缓存里尝试获取创建此Bean的单例工厂实例
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//如果获取到工厂实例
if (singletonFactory != null) {
//调用单例工厂的getObject方法返回对象实例
singletonObject = singletonFactory.getObject();
//将实例放入二级缓存里
this.earlySingletonObjects.put(beanName, singletonObject);
//从三级缓存里移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
第二个getSingleton主要是用来创建Bean的
//如果BeanDefinition为单例
if (mbd.isSingleton()) {
//这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
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.
// 显式从单例缓存中删除 bean 实例
// 因为单例模式下为了解决循环依赖,可能它已经存在了,所以将其销毁
destroySingleton(beanName);
throw ex;
}
});
// 如果是普通bean,直接返回,是FactoryBean,返回他的getObject
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
匿名工程ObjectFactory来创建对象createBean->doCreateBean
doCreateBean
该方法才是真正做事情的,重要的几个方法
- createBeanInstance 通过反射完成对象的实例化,得到半成品对象
- addSingletonFactory 为了防止循环引用,尽早持有对象的引用,把半成品对象放到三级缓存中
- populateBean 填充半成品的属性
- initializeBean 初始化半成品对象
createBeanInstance创建Bean实例
这里创建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);
//清除此Bean在二级缓存里的缓存信息
this.earlySingletonObjects.remove(beanName);
//这里为了记录注册单例的顺序
this.registeredSingletons.add(beanName);
}
}
}
populateBean填充属性
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;
}
}
// 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.
// 给InstantiationAwareBeanPostProcessors最后一次机会在属性注入前修改Bean的属性值,也可以控制是否继续填充Bean
// 具体通过调用postProcessAfterInstantiation方法,如果调用返回false,表示不必继续进行依赖注入,直接返回
// 主要是让用户可以自定义属性注入。比如用户实现一个 InstantiationAwareBeanPostProcessor 类型的后置处理器,
// 并通过 postProcessAfterInstantiation 方法向 bean 的成员变量注入自定义的信息。
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)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
//如果上面设置 continueWithPropertyPopulation = false,表明用户可能已经自己填充了
// bean 的属性,不需要 Spring 帮忙填充了。此时直接返回即可
if (!continueWithPropertyPopulation) {
return;
}
// pvs是一个MutablePropertyValues实例,里面实现了PropertyValues接口,
// 提供属性的读写操作实现,同时可以通过调用构造函数实现深拷贝
//获取BeanDefinition里面为Bean设置上的属性值
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// 根据Bean配置的依赖注入方式完成注入,默认是0,即不走以下逻辑,所有的依赖注入都需要在xml文件中有显式的配置
// 如果设置了相关的依赖装配方式,会遍历Bean中的属性,根据类型或名称来完成相应注入,无需额外配置
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
// 根据beanName进行autowiring自动装配处理
// <bean id="boyFriend" class="com.bushro.dao.impl.BoyFriend" autowire="byName"></bean>
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
//根据Bean的类型进行autowiring自动装配处理
// <bean id="boyFriend" class="com.bushro.dao.impl.BoyFriend" autowire="byType"></bean>
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
// 容器是否注册了InstantiationAwareBeanPostProcessor
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// 是否进行依赖检查,默认为false
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
//在这里会对@Autowired标记的属性进行依赖注入
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// 对解析完但未设置的属性再进行处理
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
// 依赖检查,对应depend-on属性,3.0已经弃用此属性
if (needsDepCheck) {
// 过滤出所有需要进行依赖检查的属性编辑器
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
//最终将属性注入到Bean的Wrapper实例里,这里的注入主要是供
//显式配置了autowiredbyName或者ByType的属性注入,
//针对注解来讲,由于在AutowiredAnnotationBeanPostProcessor已经完成了注入,
//所以此处不执行
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
在遍历BeanPostProcessor的时候有一个AutowiredAnnotationBeanPostProcessor
类,该类的postProcessProperties
方法来进行注入
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
//获取指定类中@Autowired相关注解的元信息
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
//对Bean的属性进行自动注入
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
在创建A的时候如果发现属性B没有实例化的话,就又会创建A的方法…
循环依赖的支持情况
Spring只能实现单例bean通过set注入或者@Autowired进行循环依赖
- 构造器循环依赖【singleton(不支持)、prototype(不支持)】
- Setter注入循环依赖【singleton、prototype(不支持)】
三级缓存除了解决单例循环依赖的问题,还解决了Bean是唯一的问题。而prototype类型的Bean不是唯一的,所以是不能使用这种方式来解决的
不支持prototype构造器循环依赖
例如:
@Component
@Scope(value = "prototype")
public class A {
private B b;
@Autowired
public A(B b) {
this.b = b;
}
}
@Component
@Scope(value = "prototype")
public class B {
private A a;
@Autowired
public B(A a) {
this.a = a;
}
}
在AbstractBeanFactory->doGetBean方法中
prototypesCurrentlyInCreation是一个ThreadLocal类型的变量。产生循环依赖的时候该变量就会有值,从而抛出异常
//如果scope为prototype并且显示还在创建中,则基本是循环依赖的情况
//针对prototype的循环依赖,spring无解,直接抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
/**
* 返回指定的原型bean当前是否正在创建中
*/
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
prototype类型的bean在spirng启动的时候并不会去初始化,只有在使用的时候才会去初始化。执行getBean的时候才会去加载。
AbstractApplicationContext->refresh->finishBeanFactoryInitialization->preInstantiateSingletons
初始话的是单例bean,并且不是懒加载的
不支持单例构造器循环依赖
例如
@Component
public class A {
private B b;
@Autowired
public A(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
@Autowired
public B(A a) {
this.a = a;
}
}
不支持的原因是构造器的实例化是在doCreateBean中的createBeanInstance
,此时还没有缓存,在实例化A的时候发现B还没有进行实例化就去执行B的构造器来实例话,这样就形成死循环了。
// 使用带参的构造函数进行装配
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
Setter注入循环依赖是在populateBean 填充半成品的属性的时候来注入,这个时候是已经有缓存了。