Spring三级缓存-解决循环依赖

82 阅读2分钟

前言

Spring中使用三级缓存来解决单例模式下属性的循环依赖问题(对于多例Bean和Prototype作用域的Bean循环依赖问题不能使用三级缓存手机解决)

一、什么是循环依赖

指在类与类之间存在相互注入的情况,如

public class A{
    @Autowired
    private B b;
}

public class B{
    @Autowired
    private A a;
}

删除代码中A里面注入了B,B里面注入了A,就产生了循环依赖

二、Spring的三级缓存

单例模式下,在第一次使用Bean时,会创建一个Bean对象,并将其放入IOC容器的缓存池中,后续使用直接从缓存池中获取即可(懒加载)。

// 一级缓存:存放的是已经初始化好的bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存:存放原始bean对象(尚未填充属性)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 三级缓存:存放bean工厂对象
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

2.1 三级缓存构建bean实例

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   // 首先尝试从一级缓存中获取
   Object singletonObject = this.singletonObjects.get(beanName);
   // 如果一级缓存中没有,isSingletonCurrentlyInCreation判断是否正在创建(还未执行初始化方法),如果是,那么尝试从二级缓存中获取
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null && allowEarlyReference) {
         synchronized (this.singletonObjects) {
            singletonObject = this.singletonObjects.get(beanName);
            // 双重判断,加锁之后需要再次判断,避免重复构建
            if (singletonObject == null) {
               singletonObject = this.earlySingletonObjects.get(beanName);
               if (singletonObject == null) {
                  // 二级缓存中不存在,那么尝试从三级缓存中获取bean工厂对象
                  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                  if (singletonFactory != null) {
                     singletonObject = singletonFactory.getObject();
                     // 对象由三级缓存移动至二级缓存,移除三级缓存中的对象
                     this.earlySingletonObjects.put(beanName, singletonObject);
                     this.singletonFactories.remove(beanName);
                  }
               }
            }
         }
      }
   }
   return singletonObject;
}

2.2 三级缓存如何解决循环依赖

spring中bean的生命周期可以简单概括为:实例化、属性注入、初始化、销毁
创建对象,完成生命周期第一步,再调用doCreateBean()方法,在该方法中有一个十分重要的步骤

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

// 省略代码。。。

   // 判断是否需要提前暴露(是否存在循环依赖问题)
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      if (logger.isTraceEnabled()) {
         logger.trace("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
      // 将对象三级缓存中
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   Object exposedObject = bean;
   try {
      // 给独享注入属性
      populateBean(beanName, mbd, instanceWrapper);
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   
// 省略代码。。。

   if (earlySingletonExposure) {
      Object earlySingletonReference = getSingleton(beanName, false);
      // 需要提前暴露,那么尝试从缓存中获取
      if (earlySingletonReference != null) {
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }

//省略代码

      }
   }

   return exposedObject;
}

---------------------------------------------------------------------------------------------

// 通过对象引用定位到堆中的对象,获取尚未赋值和初始化的对象(获取bean工厂对象)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
         exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
      }
   }
   return exposedObject;
}

---------------------------------------------------------------------------------------------
// 将获取到的工厂对象存入三级缓存中并添加beanName到已注册单例的集合中
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);
      }
   }
}

上述代码中,展示了Spring是如何将bean工厂对象存入到三级缓存中的

2.3 为什么只能解决单例的循环依赖问题

  • 多例:多实例bean是每次getBean都会创建新的对象,该bean对象无法缓存,Spring解决循环依赖是依据缓存实现的
  • prototype作用域循环依赖:IOC容器只会管理单例Bean的生命周期,对于作用域的bean,不会管理也不会缓存作用域的bean