一文搞懂 Spring 循环依赖
创作时间:
作者:
@小白创作中心
一文搞懂 Spring 循环依赖
引用
CSDN
1.
https://blog.csdn.net/u012702547/article/details/139497162
Spring框架中的循环依赖是一个常见的面试话题,也是一个容易引发系统错误的技术难点。本文将深入探讨Spring如何处理循环依赖,以及在特定场景下循环依赖的解决方案。
一、循环依赖
1.1 什么是循环依赖
循环依赖指的是两个或多个Bean互相依赖的情况。例如:
@Service
public class AService {
@Autowired
BService bService;
}
@Service
public class BService {
@Autowired
AService aService;
}
这种依赖关系可以表示为:
1.2 循环依赖的类型
循环依赖主要有三种形态:
- 两个Bean互相依赖(如上例)
- 三个或更多Bean形成依赖环
- Bean自我依赖
一般来说,如果代码中出现循环依赖,说明设计上可能存在问题。虽然Spring默认可以处理循环依赖,但这种代码结构并不推荐。
二、循环依赖解决思路
2.1 解决思路
Spring通过引入三级缓存机制来解决循环依赖问题:
- earlySingletonObjects(二级缓存):存储通过反射创建但尚未完成初始化的Bean实例。
- singletonObjects(一级缓存):存储已完成初始化的Bean实例。
- singletonFactories(三级缓存):存储创建Bean实例的工厂对象,用于处理AOP代理。
具体流程如下:
- 创建AService实例时,先通过反射创建一个原始的AService对象,并存入二级缓存。
- 在给AService设置属性时,发现需要BService,于是创建BService。
- 创建BService时发现需要AService,从二级缓存中获取AService的原始对象使用。
- BService创建完成后,将其赋值给AService,此时AService和BService都创建完成。
2.2 存在AOP怎么办
当涉及AOP时,情况会更复杂。Spring通过三级缓存机制提前处理AOP,确保依赖关系正确:
- 在创建Bean时,如果需要AOP代理,先在三级缓存中保存一个生成代理对象的工厂。
- 在处理循环依赖时,从三级缓存中获取工厂生成代理对象,而不是直接使用原始Bean。
- 最终确保所有依赖都指向正确的代理对象。
2.3 小结
Spring解决循环依赖的关键在于:
- 提前暴露:将未完成初始化的Bean提前暴露给其他Bean使用。
- 提前AOP:在处理循环依赖时提前处理AOP代理。
三、特殊情况
3.1 基于构造器注入
如果依赖是通过构造器注入的,Spring无法解决循环依赖,因为创建Bean时就需要完整的依赖对象。例如:
@Service
public class AService {
BService bService;
public AService(BService bService) {
this.bService = bService;
}
}
@Service
public class BService {
AService aService;
public BService(AService aService) {
this.aService = aService;
}
}
3.2 prototype对象
当循环依赖的Bean作用域为prototype时,也会导致循环依赖失败,因为每次都需要现场创建Bean。
3.3 @Async
带有@Async注解的Bean产生循环依赖时,由于AOP处理的特殊性,Spring也无法自动解决。
四、@Lazy注解解决方案
通过添加@Lazy注解,可以解决上述三种特殊场景的循环依赖问题。@Lazy注解的工作原理是为依赖对象生成一个代理对象,延迟实际对象的加载。
例如:
@Service
public class AService {
@Autowired
@Lazy
BService bService;
@Async
public void hello() {
bService.hello();
}
}
原理分析
@Lazy注解的处理主要发生在属性注入过程中:
- 在
resolveFieldValue
方法中,调用resolveDependency
方法解析依赖。 - 在
resolveDependency
方法中,检查是否需要延迟加载(通过getLazyResolutionProxyIfNecessary
方法)。 - 如果需要延迟加载,构建一个代理对象(通过
buildLazyResolutionProxy
方法)。
代理对象在需要时才会真正加载依赖Bean,从而避免了循环依赖问题。
总结
虽然Spring提供了多种机制来处理循环依赖,但在实际开发中,还是应该尽量避免循环依赖的出现,通过合理的模块划分和设计模式来优化代码结构。
本文内容参考自CSDN,原文链接:https://blog.csdn.net/u012702547/article/details/139497162
热门推荐
上海交大突破高功率燃料电池电堆关键技术,入选全球新能源汽车创新技术
高速上开车,车速控制在什么范围更安全?老司机:不固定,看情况
酸奶一天喝多少合适
艾草根的副作用与禁忌
教师转行考公务员的条件详解与报考公务员资格限制指南:聚焦人群与限制解读
杭州至上海“超级高铁”有新进展!或将缩短列车运行时间至15分
18位中国工程院院士!历届国家最高科学技术奖获得者一览
蛇胆疮怎么治好得快
糖尿病患者应该挂哪个科室?一文详解五大科室诊疗范围
行业轮动是什么意思,一种主动型交易策略
在网络攻击中使用的法律武器有哪些
云南必点的6道特色菜,老板以为你是云南人
两情相悦齐心协力奋斗一个家,是两人必须要的经过
砂石垫层施工要求与主要作用原理,3:7级配砂石怎样配
中华人民共和国十大大将——许光达
个人破产对征信有影响吗?公司破产如何赔偿员工?
如何增加财产性收入?增加本金!提高收益率!
纳米防雾的原理
撞人后跑了又回来了算逃逸吗
韩国留学签证办理流程与费用明细
口头协议存在哪些法律风险
核心城市土拍热度持续升温,核心城市土地市场为何升温?
香港的骑行路线推荐
灰烬战线机娘角色培养攻略:从零开始
格式条款的概念和特征有哪些
雅思5分水平怎么样?雅思评分标准详解
英国分为哪几个地区?
新生儿可以睡枕头吗?专家建议来了
九价HPV疫苗有望只打一针?保护效力仍需研究
2024年量子计算性能评估基准研究报告