Spring的循环依赖
Spring循环依赖
1 循环依赖解决及概述
(1)循环依赖的问题描述和Spring解决流程
@Component
public class A{
@Autowired
private B b;
}
@Component
public class B{
@Autowired
private A a;
}
循环依赖, 形式如上.
spring按照顺序初始化Bean:
第一步创建A, 构建A的BeanFactory放置在三级缓存中,然后实例化A, 注入属性, 发现A需要B, 在一级二级三级缓存中没有发现B, 开始创建B;
第二步, 创建B, 构建B的BeanFactory放置在三级缓存中, 然后实例化, 注入属性, 发现B需要A, 于是从一级二级缓存寻找A, 无果, 在三级缓存中找到A的BeanFactory, 从中取出 A(或A的代理对象), 然后将A放置在二级缓存中, 将其从三级缓存中删除, B初始化完成, 进入到一级缓存;
第三步, A从一级缓存中获取到初始化完成的B, A也初始化完成, 进入到一级缓存, A 和 B都初始化完成.
需要注意的是:
三级缓存的目的主要是延迟生成代理对象.
正常情况下, Bean的代理对象是在初始化完成后才生成的, 然而, 如果产生了循环依赖, spring出于开发者考虑, 如果对于Bean进行了AOP代理, 那么对于这个Bean的依赖, 开发者也必然是想注入其代理对象的.
因此, 在产生循环依赖的情况下, Bean的代理对象会提前生成, 将该代理对象传递给依赖者.
亦即是说, Bean的代理对象生成可能会有两个时间点.
在第二步中, spring会进行判断, 如果目标对象被代理, 则提前生成代理对象, 否则在对象初始化完成后再生成代理对象.
产生循环依赖的情况下, 被依赖对象的初始化完成需要从三级缓存中**取出BeanFactory中的实例引用, 此时会作一个判断, **判断该实例是否被增强(AOP), 来决定是否返回代理对象(提前生成代理对象).
在不产生循环依赖的情况下,不需要从beanFactory取出, 因此不需要进行该判断, 正常情况下, 在bean初始化完成后,进行代理对象的生成.
二级缓存的目的则是解决多个Bean相互依赖的问题
@Component
public class A{
@Autowired
private B b;
@Autowired
private C c;
}
@Component
public class B{
@Autowired
private A a;
}
@Component
public class C{
@Autowired
private C c;
}
如上所示, 当B进行初始化后, A被放置在二级缓存中, 等C进行初始化的时候, 从二级缓存中取出.
参考:
Spring是怎么解决循环依赖的? - 简书 (jianshu.com)
面试必杀技,讲一讲Spring中的循环依赖 - 程序员DMZ - 博客园 (cnblogs.com)
spring + spring mvc + tomcat 面试题(史上最全) - 疯狂创客圈 - 博客园 (cnblogs.com)
2 关于循环依赖的种类
spirng中注入的方式分为:
- 基于构造器的注入
- 基于setter的注入
另外又涉及到Bean的作用域:
- 单例-singleton
- 原型-prototype
- request
- session
- globle-session
因此循环依赖可以分为三类:
- 单例状态下基于setter注入方式的循环依赖
- 单例状态下基于构造器的注入方式的循环依赖
- 多例状态下的循环依赖
对于多例状态下的循环依赖,无法解决
基于构造器注入方式的单例循环依赖,可以采用@lazy的方式,延迟加载以解决
spring本身的机制,只能解决基于setter方式注入的单例循环依赖
资料来源
Spring面试题必知必会-循环依赖概览:
https://blog.csdn.net/a745233700/article/details/80959716
Spring循环依赖详解:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)