spring是如何解决循环依赖的(二级缓存不行吗)?

首先我们要知道什么是循环依赖,在 Spring 中,循环依赖指的是两个或多个 Bean 之间相互引用(如 A 依赖 B,B 又依赖 A)。看起来像死锁一样是吧。

核心思路:

Spring 通过三级缓存机制解决了单例 Bean 之间的循环依赖问题。通过三级缓存,存储不同的bean示例,通过提前暴露未完全初始化的bean 打破循环,流程如下:

三级缓存的定义:

一级缓存(singletonObjects):

存储完全初始化的bean实例,是最终可直接使用的实例。

二级缓存(earlySingletonObjects

存储提前暴露的未完全初始化的单例bean(已经实例化但是未完成属性注入和初始化方法)

三级缓存(singletonFactories

存储bean的工厂对象,用于需要时生成bean的早起代理对象或者原始实例。

解决循环依赖的步骤(以 A 依赖 B,B 依赖 A 为例)

  1. 初始化 A

    • 调用 getBean(A),检查三级缓存均无 A,开始创建 A。
    • 实例化 A(执行构造方法),但未注入属性和执行初始化方法。
    • 将 A 的工厂对象(ObjectFactory)放入三级缓存(singletonFactories),用于后续生成早期实例。
  2. A 依赖 B,触发 B 的初始化

    • A 需要注入 B,调用 getBean(B),检查三级缓存均无 B,开始创建 B。
    • 实例化 B,将 B 的工厂对象放入三级缓存。
    • B 需要注入 A,调用 getBean(A)
  3. 获取 A 的早期实例

    • 查找 A 时,一级缓存无,但三级缓存有 A 的工厂对象。
    • 通过工厂对象生成 A 的早期实例(若 A 需要代理,此处会生成代理对象),并将其从三级缓存移至二级缓存(earlySingletonObjects)。
    • 将 A 的早期实例注入 B,B 完成属性注入和初始化,放入一级缓存(singletonObjects)。
  4. 完成 A 的初始化

    • B 初始化完成后,将 B 注入 A,A 完成属性注入和初始化,放入一级缓存。
    • 最终,A 和 B 均在一级缓存中,循环依赖解决。

关键细节

  • 仅支持单例 Bean:原型(prototype)Bean 不支持循环依赖,因为原型 Bean 每次获取都会新建,无法提前暴露。
  • 构造器循环依赖无法解决 :若循环依赖发生在构造方法(如 A 的构造器依赖 B,B 的构造器依赖 A),Spring 会直接抛出 BeanCurrentlyInCreationException,因为此时 Bean 尚未实例化,无法提前暴露。
  • 三级缓存的作用:三级缓存通过工厂对象延迟生成早期实例(尤其是代理对象),避免提前生成代理导致的问题(如初始化方法执行时机错误)。
相关推荐
JAVA面经实录91713 小时前
Java企业级工程化·终极完整版背诵手册(无遗漏、全覆盖、面试+落地通用)
java·开发语言·面试
许彰午15 小时前
CacheSQL(二):主从复制——OpLog 环形缓冲区与故障自动恢复
java·数据库·缓存
Bat U16 小时前
JavaEE|多线程初阶(七)
java·开发语言
掌心向暖RPA自动化19 小时前
如何获取网页某个元素在屏幕可见部分的中心坐标影刀RPA懒加载坐标定位技巧
java·javascript·自动化·rpa·影刀rpa
日取其半万世不竭19 小时前
Minecraft Java版社区服务器搭建教程(Linux,适合新手)
java·linux·服务器
TeamDev20 小时前
JxBrowser 9.0.0 版本发布啦!
java·前端·混合应用·jxbrowser·浏览器控件·跨平台渲染·原声输入
AI人工智能+电脑小能手20 小时前
【大白话说Java面试题】【Java基础篇】第24题:Java面向对象有哪些特征
java·开发语言·后端·面试
AI人工智能+电脑小能手20 小时前
【大白话说Java面试题】【Java基础篇】第25题:JDK1.8的新特性有哪些
java·开发语言·后端·面试
likerhood21 小时前
SLF4J: Failed to load class “StaticLoggerBinder“ 解决
java·log4j·maven
早日退休!!!21 小时前
大模型推理瓶颈七层分析模型
java·服务器·数据库