Spring 三级缓存

一、序言

本文介绍 Spring 中如何使用三级缓存解决循环依赖问题的。

二、什么是 Spring 循环依赖

image.png
在 Spring 中,循环依赖是指多个 Bean 之间相互依赖,形成了一个闭环。例如上图:

  1. Bean A 若依赖自身,形成循环依赖
  2. Bean A 依赖 Bean B,Bean B 又依赖 Bean A,形成了循环依赖
  3. Bean A 依赖 Bean B,Bean B 依赖 Bean C,而 Bean C 依赖 Bean A,形成了循环依赖

我们接下来以第二个图的循环依赖为例展开讨论(即 A 依赖 B,B 又依赖 A)。

三、Spring 三级缓存处理流程分析

在这里插入图片描述

Spring 创建 Bean A 与 Bean B 的流程如下:

  1. 从三级缓存中获取 Bean A
  2. 获取成功,直接返回
  3. 获取失败,开始创建 Bean A
  4. 实例化 Bean A
  5. 将实例化之后的 Bean A 放入三级缓存
  6. Bean A 准备填充属性 Bean B
  7. 从三级缓存中查找 Bean B
  8. Bean B 存在,Bean A 属性填充成功,Bean A 初始化成功
  9. Bean B 不存在,开始创建 Bean B
  10. 实例化 Bean B
  11. 将实例化之后的 Bean B 放入三级缓存
  12. Bean B 准备填充属性 Bean A
  13. 从三级缓存中查找 Bean A(一定能找到,因为之前已将 Bean A 放入了三级缓存)
  14. Bean B 属性填充成功,Bean B 初始化成功
  15. Bean A 继续执行填充属性的步骤
  16. Bean A 属性填充成功(Bean B 已存在),Bean A 初始化成功

至此,流程分析结束。

四、为什么是三级缓存

我们将三级缓存执行流程图中的三级缓存改成一级缓存(即只有一层缓存)

在这里插入图片描述

该逻辑流程似乎依旧能够解决循环依赖。没错,其实只需要一级缓存便可以解决循环依赖。那么,其余两层缓存的作用究竟是什么呢?

4.1 循环依赖中的 AOP

@Component
public class A {
    // 注入 B
    @Autowired
    private B b;
}

@Component
public Class B {
    // 注入 A
    @Autowired
    private A a;
}

// A 的切面类
@Aspect
public class AAspect {

    @Around("execution(* A(..))")
    public Object handle() {}
}

思考一下,如果上述三个类注入到 Spring 中,Spring 该如何去创建 Bean?
如果按照我们之前的流程:Bean A 会先放入缓存,接着 Bean B 创建,Bean B 发现依赖 Bean A。此时,Bean B 会去缓存中寻找 Bean A。
在上述的代码中,由于 Bean A 有一个切面,所以理应是去缓存中寻找 Bean A 的代理。如果只有一级缓存,该如何解决这个问题呢?

4.2 三级缓存解决方案

针对 AOP 的循环依赖问题,Spring 给出的是三级缓存方案。这个方案的具体内容是:

在这里插入图片描述

  1. Bean A 实例化,向三级缓存中放入其 ObjectFactory 包装的对象
  2. Bean A 填充属性 Bean B,Bean B 在缓存中未找到
  3. Bean B 实例化,向三级缓存中放入其 ObjectFactory 包装的对象
  4. Bean B 填充属性 Bean A,发现三级缓存中有 Bean A,调用 getObject() 获取对象
  5. Bean A 若有代理对象,则返回的是代理对象;反之,返回原始 Bean A
  6. 半成品的代理/原始 Bean A 放入二级缓存,移除三级缓存中的内容
  7. 初始化 Bean B 完成
  8. 完整 Bean B 放入一级缓存,移除三级缓存 Bean B 的 ObjectFactory
  9. 初始化 Bean A 完成
  10. 完整 Bean A 放入一级缓存,移除二级缓存中代理/原始的 Bean A

两个问题:

  1. 为什么使用 ObjectFactory 包装?

    之所以使用 ObjectFactory 去包装原始的 Bean 对象,这是因为 Bean 可能会有代理对象(例如:上文中 Bean A 存在切面)。究竟是获取代理 Bean 还是原始 Bean,可以统一让 ObjectFactory 处理。

  2. 为什么 ObjectFactory#getObject() 方法获取的代理/原始 Bean 对象不直接放入一级缓存/三级缓存?

    ObjectFactory#getObject() 获取的代理/原始 Bean 是一个半成品的对象(属于中间的一种状态),而一级缓存放的是完整的对象,三级缓存中放的是获取对象的 Factory。无论是从设计还是从逻辑上去考量都应该将这种状态单独处理。

相关推荐

  1. Spring三级缓存

    2024-04-13 10:00:02       35 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-13 10:00:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-13 10:00:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-13 10:00:02       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-13 10:00:02       20 阅读

热门阅读

  1. HBase Shell命令

    2024-04-13 10:00:02       12 阅读
  2. Vivado Design Suite中的Routing优化

    2024-04-13 10:00:02       19 阅读
  3. 洛谷 P2863

    2024-04-13 10:00:02       18 阅读
  4. 虾基因组学:概念、历史、现状与展望

    2024-04-13 10:00:02       21 阅读
  5. PCB工艺规范及PCB设计安规原则

    2024-04-13 10:00:02       17 阅读