Spring是怎么解决循环依赖的

150 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情

spring的循环依赖是什么

假设有A,B两个类,A类有B属性,B类有A属性,那么当spring扫描到这两个类,如何对这两个类进行实例化

public class A{
	B b;
}
public class B{
	A a;
}

如上面代码,A对象有一个属性是B,B对象有一个属性是A,当A对象生成的时候依赖B对象,B对象生成的时候需要依赖A对象,这个时候就很容易形成一个闭环,如果这样一直死循环下去。

循环依赖的解决

Spring内部通过三级缓存来解决循环依赖的问题

所谓三级缓存,就是spring容器解决循环依赖的三个map:

一级缓存:singletonObjects,是一个ConcurrentHashMap,也叫单例池,存放已经经历了完整生命周期的Bean对象;

二级缓存:earlySingletonObjects,是一个HashMap,存放早期暴露出来的Bean对象,Bean的生命周期未结束,属性还未填充完整;

三级缓存:singletonFactories,第三级缓存value是一个工厂,三级缓存存放可以生成bean的工厂。

image.png

注意:只有单例的Bean会通过三级缓存来解决循环依赖的问题,而非单例的Bean,每次从容器获取都是一个新的对象,都会重新创建,所以非单例的Bean是没有缓存的,不会放在三级缓存中。

源码分析

Spring通过AbstractBeanFactory#doGetBean来获取Bean对象,尝试去缓存中获取对象:一级>二级>三级

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

   ...
         //创建单例bean
         if (mbd.isSingleton()) {
            //此处getSingleton不同于上面的getSingleton方法:
            //前面的getSingleton主要是从缓存中获取对象,如果有直接返回;
            //后面的getSingleton获取单例对象,用于触发构建bean
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  //进入创建bean的逻辑
                  return createBean(beanName, mbd, args);
               }
         ...
   return (T) bean;     
}

通过AbstractBeanFactory#getSingleton方法,最终会调用DefaultSingletonBeanRegistry#getSingleton ,尝试从缓存中获取,

先从一级缓存中获取,一级缓存中没有获取到并且该bean处于正在创建阶段,去二级缓存中获取,二级缓存中也没有获取到对象,且allowEarlyReference为true,直接从三级缓存中获取ObjectFactory对象,把对象放在二级缓存,并且ObjectFactory包装对象从三级缓存中删除掉

image.png

因为第一次去缓存中获取不到对象,所以还会走下面的getSingleton–>createBean–>doCreateBean逻辑。

在createBean之前把Bean标记成正在创建,作为循环依赖的出口:

image.png DefaultSingletonBeanRegistry#addSingletonFactory:用于把对象包装成ObjectFactory暴露到三级缓存singletonFactories中,解决循环依赖

image.png