• Spring 源码分析(五)——Spring三级缓存的作用分别是什么?


    Spring三级缓存是经典面试题,也会看到一些文章讲三级缓存与循环依赖之的关系。那么,三级缓存分别存储的什么呢?他们的作用又分别是什么?

    一、一、二级缓存

    一级缓存是一个名为 singletonObjectsConcurrentHashMap,用于存储已经创建完成的 Bean。其作用也是最明显的,获取 Bean 时最优先从一级缓存中获取。

    二级缓存是一个名为 earlySingletonObjectsConcurrentHashMap,用于存储还未创建完成的 Bean。在循环依赖时,可以从中预先获取未创建完成的 Bean,从而解决循环依赖问题

    二、三级缓存

    三级缓存是一个名为 singletonFactoriesConcurrentHashMap,用于存储创建指定 Bean 的工厂。其作用是为了在 AOP 代理时解决循环依赖。

    更具体可以说,是为了在 AOP 代理时,既解决循环依赖,又最大满足 Spring 制定的 Bean 生命周期流程。

    是的,三级缓存一定程度上说,他提前了 Bean 生命周期的流程。

    熟悉 Bean 的生命周期可知,Bean 初始化是经过了实例化(createBeanInstance)、属性注入(populateBean)、后置处理器与生命周期方法(initializeBean)三个步骤处理,最终才得到一个创建完成的 Bean

    在循环依赖时,在属性注入(populateBean)步骤就要解决依赖问题,而 AOP 代理是在后置处理器与生命周期方法(initializeBean)步骤中通过后置处理器实现的。如果严格按照 Bean 的这个生命周期执行,依赖注入是无法注入代理之后的对象的。

    假设,A 和 B 互相依赖,A 经过 AOP 代理,最终的 BeanAa ,那么严格按照 Bean 的生命周期最终属性注入的结果如下:

    getBean——取得 A Bean,在 doCreateBean 方法中开始创建 Bean 操作。

    createBeanInstance——实例化 A Bean。

    populateBean——为 A Bean 设置参数,并调用 getBean 方法创建 B Bean。

    == createBeanInstance——实例化 B Bean。

    == populateBean——为 B Bean 设置参数,并调用 getBean 方法获得未构造完全的 A Bean。

    initializeBean——为A Bean 执行后置处理器和生命周期方法,并完成 AOP 代理,获得最终的 Aa Bean。

    从上面步骤可以发现,B 中注入的是代理之前的 A 对象,而实际上需要被注入的是代理之后的 Aa 对象。

    要解决上述 AOP 代理的问题其实也很简单,只要在 createBeanInstance 步骤中完成 AOP 代理其实一切问题就迎刃而解。但是这并不符合 Bean 的生命周期设定,而且循环依赖的场景很少见,在实例化时也无法判断这个 Bean 是否被循环依赖了,直接将 AOP 代理全部提前执行不合适。

    最终,Spring 引入了三级缓存,在实例化对象之后,进行属性注入之前,将实现 AOP 代理的步骤封装为 Bean 工厂放进三级缓存。如果这个对象被循环依赖了,则使用工厂提前进行 AOP 代理,如果没有被循环依赖,则这个工厂就不会被使用。

    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");
      }
      // 创建bean工厂注入到三级缓存
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
      populateBean(beanName, mbd, instanceWrapper);
      exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    阅读 getEarlyBeanReference 方法逻辑,可以看到其中遍历找到了实现了SmartInstantiationAwareBeanPostProcessor 接口的后置处理器,并执行了其中的 getEarlyBeanReference 方法,提前完成了代理操作,获取到了代理后的最终得对象。

    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;
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
          }
        }
      }
      return exposedObject;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在获取对象时,getEarlyBeanReference 便按三级缓存的顺序执行操作,如果在三级缓存中获取到 Bean 工厂,则通过工厂获取对象。

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
      // Quick check for existing instance without full singleton lock
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
          synchronized (this.singletonObjects) {
            // Consistent creation of early reference within full singleton lock
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
              singletonObject = this.earlySingletonObjects.get(beanName);
              if (singletonObject == null) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                  singletonObject = singletonFactory.getObject();
                  this.earlySingletonObjects.put(beanName, singletonObject);
                  this.singletonFactories.remove(beanName);
                }
              }
            }
          }
        }
      }
      return singletonObject;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    软件测试面试百问:如何测试App性能?
    zabbix自定义监控、钉钉、邮箱报警
    OpenCV图像处理学习二十一,直方图比较方法
    论文写作--30条总结
    本地demo服务器搭建计划——(二)服务中心consul安装&防火墙配置
    基于SSH的CRM客户关系管理系统
    Shell脚本之正则表达式
    java8 新特性 -Optional的常见用法
    《代码大全2》第1章 欢迎进入软件构建的世界
    从零开始自学vue3笔记(一):搭建步骤
  • 原文地址:https://blog.csdn.net/nineya_com/article/details/133394925