一、循环依赖是怎么产生的最经典的场景就是Bean 之间相互引用ComponentclassA{AutowiredprivateBb;}ComponentclassB{AutowiredprivateAa;} 发生了什么Spring 创建 AA 依赖 B → 去创建 B创建 B 时发现依赖 A → 又要创建 AA 还没创建完成 → 卡住 →循环依赖产生 本质Bean 创建过程中形成闭环依赖二、Spring 为什么“默认能解决”关键点只支持“单例 setter/字段注入”的循环依赖Spring 的解决方案核心是⭐ 三级缓存重点面试高频singletonObjects 一级缓存完全初始化好的BeanearlySingletonObjects 二级缓存提前暴露的Bean半成品 singletonFactories 三级缓存Bean工厂用于生成代理对象三、解决流程核心机制用 A 和 B 举例 步骤拆解创建 A此时是“半成品”把 A 的ObjectFactory 放入三级缓存A 依赖 B → 去创建 B创建 B 时依赖 A → 尝试获取 A从三级缓存拿到 A 的“早期引用”可能是代理对象B 创建完成回过头来完成 A 的初始化 这样就绕开了“死循环”四、为什么需要三级缓存很多人会问二级缓存不够吗答案不够因为要解决AOP 代理问题 关键点如果 A 需要被代理比如事务、AOP不能直接暴露原始对象必须暴露代理对象 三级缓存ObjectFactory就是为了在“提前暴露”时可以决定返回普通对象还是代理对象五、哪些情况解决不了Spring 不是万能的这些情况会直接报错❌ 1. 构造器注入Constructor InjectionclassA{publicA(Bb){}}classB{publicB(Aa){}} 原因构造器必须“完整对象”不能用半成品 → 无法提前暴露❌ 2. 原型prototype作用域 多例BeanScope(prototype) 原因Spring 不缓存 prototype Bean → 没法用三级缓存❌ 3. 循环依赖链过于复杂含 FactoryBean / 动态代理比如A → B → C → A或者涉及FactoryBeanBeanPostProcessor动态代理AOP 复杂嵌套 为什么不行 提前暴露的对象可能不是最终形态还没代理或者代理链没构建完成→ 最终导致Bean 创建异常 / 代理错乱❌ 4. AOP 循环依赖的“隐形坑”例如ServiceclassAService{AutowiredprivateBServicebService;TransactionalpublicvoidmethodA(){}}⚠️ 问题点提前暴露的是“早期对象”AOP 代理可能还没织入 结果可能是事务、切面不生效这种不是启动报错而是运行时“悄悄出 bug”❌ 5. Spring Boot 2.6 默认限制从Spring Boot 2.6开始spring.main.allow-circular-referencesfalse 默认禁止循环依赖 表现直接启动失败哪怕是 setter 注入六、如何解决循环依赖问题✅ 方法1改成 setter 注入推荐AutowiredpublicvoidsetB(Bb){this.bb;} 让 Spring 有机会“先创建半成品”✅ 方法2使用 Lazy常用技巧AutowiredLazyprivateBb; 延迟加载相当于“等真正用到你再创建”✅ 方法3拆分设计最佳实践比如A→CB→C 引入中间层打断循环✅ 方法4使用接口解耦A→IBB→IA 降低耦合避免强依赖闭环✅ 方法5手动获取 BeanApplicationContext.getBean() 不推荐但某些场景可用