Spring三级缓存

2021-05-08   


Spring三级缓存

场景

  一直以为在Spring中出现循环依赖是个无解的问题,只能重新梳理bean之间的依赖关系避免形成循环依赖。今天突然发现ServiceA依赖了ServiceB,而ServiceB又依赖了ServiceA,但是服务却可以正常运行。这么看来,Spring是解决了循环依赖问题?一番搜索发现Spring使用了名为三级缓存的东西来解决循环依赖问题,简单记录下原理。

源码
  /** Cache of singleton objects: bean name to bean instance. */
  private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

  /** Cache of singleton factories: bean name to ObjectFactory. */
  private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

  /** Cache of early singleton objects: bean name to bean instance. */
  private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  • singleObjects:完成初始化的单例对象的cache,这里的bean经历过实例化->属性填充->初始化,以及各种后置处理。(一级缓存)
  • earlySingleObjects:存放原生的bean对象(完成实例化但尚未进行属性填充及初始化),仅仅作为指针提前曝光,被其他bean引用。(二级缓存)
  • singlefactories:在bean实例化之后,属性填充及初始化之前,如果允许提前曝光,Spring会将实例化之后的bean提前曝光,也就是把该bean转换为beanFactory并加入到singlefactories。(三级缓存)
  1. AbstractBeanFactory#doGetBean,使用getSingleton(beanName)获取到bean实例。
  @Nullable
  protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //尝试从一级缓存获取
    Object singletonObject = this.singletonObjects.get(beanName);
    //获取不到并且bean正在创建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
        //二级缓存获取
        singletonObject = this.earlySingletonObjects.get(beanName);
        //二级缓存也没用并且允许获取早期引用
        if (singletonObject == null && allowEarlyReference) {
          //三级缓存获取ObjectFactory
          ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
          if (singletonFactory != null) {
            //获取bean实例
            singletonObject = singletonFactory.getObject();
            //放入二级缓存
            this.earlySingletonObjects.put(beanName, singletonObject);
            //从三级缓存删除
            //也就是说,对于一个单例bean,ObjectFactory#getObject只会调用一次
            //获取到早期bean之后,把这个bean实例从三级缓存升级到二级缓存
            this.singletonFactories.remove(beanName);
          }
        }
      }
    }
    return singletonObject;
  }
  1. 可以看到二级缓存中的bean是在getSingleton方法中put进去的,所以创建bean实例之后先是放到了三级缓存。在第一点中从三级缓存中仍有可能未获取到bean,此时会调用另一个getSingleton方法,它的入参有一个lambda。
    三级缓存1
    可以看到这个方法在一级缓存中获取不到bean时,会调用外部传入的lambda,即第三点的createBean逻辑。
  public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized (this.singletonObjects) {
      Object singletonObject = this.singletonObjects.get(beanName);
      //一级缓存拿不到
      if (singletonObject == null) {
        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!)");
        }
        if (logger.isDebugEnabled()) {
          logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
        }
        beforeSingletonCreation(beanName);
        boolean newSingleton = false;
        boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
        if (recordSuppressedExceptions) {
          this.suppressedExceptions = new LinkedHashSet<>();
        }
        try {
          //调用外部传入的lambda,即下面第3点的createBean逻辑
          //获取完成实例化的bean
          //此时bean的实例已经存在于二级或三级缓存
          //未出现循环依赖的情况,bean的实例一直在三级缓存,出现循环依赖的情况,将bean实例从三级缓存升级到二级缓存
          singletonObject = singletonFactory.getObject();
          newSingleton = true;
        }
        catch (IllegalStateException ex) {
          // Has the singleton object implicitly appeared in the meantime ->
          // if yes, proceed with it since the exception indicates that state.
          singletonObject = this.singletonObjects.get(beanName);
          if (singletonObject == null) {
            throw ex;
          }
        }
        catch (BeanCreationException ex) {
          if (recordSuppressedExceptions) {
            for (Exception suppressedException : this.suppressedExceptions) {
              ex.addRelatedCause(suppressedException);
            }
          }
          throw ex;
        }
        finally {
          if (recordSuppressedExceptions) {
            this.suppressedExceptions = null;
          }
          afterSingletonCreation(beanName);
        }
        //因为createBean逻辑中进行了initializeBean操作,所有此时相当于已经新初始化了一个单例bean,加入一级缓存
        if (newSingleton) {
          addSingleton(beanName, singletonObject);
        }
      }
      return singletonObject;
    }
  }
  1. 现在来看AbstractAutowireCapableBeanFactory#createBean方法,这里最终调用的是该类的doCreateBean方法。此时将bean加入到三级缓存,若出现循环依赖的情况则会从三级缓存升级到二级缓存,即1里面的逻辑。
  //单例、允许循环依赖、正在创建
  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));
  }
  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);
      }
    }
  }

接着去执行了initializeBean初始化操作。

  // Initialize the bean instance.
  Object exposedObject = bean;
  try {
    populateBean(beanName, mbd, instanceWrapper);
    //初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);
  }
  catch (Throwable ex) {
    if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
      throw (BeanCreationException) ex;
    }
    else {
      throw new BeanCreationException(
          mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
    }
  }
  1. 回到第二点的getSingleton方法,此时已经获取到了一个完成初始化的bean对象,将它加入一级缓存。
    三级缓存2
    三级缓存3
  2. 为什么需要三级缓存而不是二级缓存
    还是在AbstractAutowireCapableBeanFactory#doCreateBean,initializeBean方法中,由于BeanPostProcessor的postProcessBeforeInitialization、postProcessAfterInitialization方法可能把创建的bean替换从而返回一个新对象。
    三级缓存4
    回到第3点的addSingletonFactory方法,它的第二个参数就是一个ObjectFactory,这个ObjectFactory最终放入了三级缓存。其中提供了一个getEarlyBeanReference的埋点方法,通过这个埋点方法,它允许我们把需要替换的bean,提早替换出来。
  protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
          SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
          //用了beanPostProcessor的一个埋点方法
          exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
        }
      }
    }
    return exposedObject;
  }

Spring的AOP功能是通过生成动态代理类来实现的,我们最终使用的也是代理类而不是原始类实例。而代理类的创建就是在initializeBean方法的postProcessAfterInitialization埋点中。具体类为AbstractAutoProxyCreator。

  //出现循环依赖的情况,getEarlyBeanReference会被先调用
  @Override
  public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    //当前类放入earlyProxyReferences
    this.earlyProxyReferences.put(cacheKey, bean);
    //返回代理实例
    return wrapIfNecessary(bean, beanName, cacheKey);
  }

  @Override
  public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      //出现循环依赖的情况,earlyProxyReferences中存放了当前类,因此不会返回代理对象
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
        //没有出现循环依赖的情况,返回代理实例
        return wrapIfNecessary(bean, beanName, cacheKey);
      }
    }
    return bean;
  }
其他

  Spring对于属性注入(setter注入)的循环依赖问题,可以通过三级缓存来解决,但是它无法解决构造器注入的循环依赖,解决构造器注入的循环依赖可以通过@Lazy注解(延迟加载)。

Q.E.D.