Spring 循环依赖:别让“相互等待”拖慢你的应用启动
引言
在Java的Spring框架中,循环依赖(Circular Dependency)是一个常见的“坑”,尤其是对于初学者来说。想象一下,两个人面对面站着,都等着对方先走一步,结果谁都动不了,这就是循环依赖的直观感受。在Spring中,当两个或多个bean相互依赖时,就可能陷入这样的困境。今天,我们就来聊聊Spring中的循环依赖,用简单的比喻和实际的demo,帮助大家理解这个问题及其解决方案。
一、什么是Spring循环依赖?
比喻:想象你和小伙伴在玩“你先走”的游戏,你们都站在起点,等着对方先迈出第一步。在Spring中,这就像是两个bean都在等待对方被初始化,以完成自己的初始化。当这种“相互等待”的情况发生时,Spring容器就会抛出异常,提示存在循环依赖。
Demo说明:
@Component
public class BeanA {
private final BeanB beanB;
@Autowired
public BeanA(BeanB beanB) {
this.beanB = beanB;
}
}
@Component
public class BeanB {
private final BeanA beanA;
@Autowired
public BeanB(BeanA beanA) {
this.beanA = beanA;
}
}
在这个demo中,BeanA
和BeanB
都通过构造器注入了对方,形成了循环依赖。运行这个Spring Boot应用时,你会遇到BeanCurrentlyInCreationException
异常。
二、Spring如何解决循环依赖?
Spring框架默认通过三级缓存机制来解决循环依赖问题。这个机制允许Spring在bean完全初始化之前,提供一个早期引用来满足其他bean的依赖需求。
比喻:想象你在餐厅点餐,厨师告诉你,虽然你的牛排还没做好,但你可以先拿一个号码牌,等牛排做好了,厨师会叫你。在Spring中,这个“号码牌”就是早期引用,它允许bean在完全初始化之前被注入到其他bean中。
Demo说明(简化版,实际机制涉及更多细节):
// 假设这是Spring容器内部的一部分逻辑
Map<String, Object> singletonObjects = new HashMap<>(); // 一级缓存
Map<String, Object> earlySingletonObjects = new HashMap<>(); // 二级缓存
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(); // 三级缓存
// 当Spring尝试创建BeanA时
Object beanA = singletonFactories.get("beanA").getObject(); // 从三级缓存中获取早期引用
earlySingletonObjects.put("beanA", beanA); // 放入二级缓存
// 当Spring尝试创建BeanB时
Object beanB = singletonFactories.get("beanB").getObject(); // 从三级缓存中获取早期引用
earlySingletonObjects.put("beanB", beanB); // 放入二级缓存
// 完成BeanA的初始化,并放入一级缓存
singletonObjects.put("beanA", fullyInitializedBeanA);
// 完成BeanB的初始化,并放入一级缓存
singletonObjects.put("beanB", fullyInitializedBeanB);
注意:这个demo是为了说明概念而简化的,实际的Spring源码实现要复杂得多。
三、如何避免循环依赖?
虽然Spring提供了解决循环依赖的机制,但这并不意味着我们应该依赖它。循环依赖通常表明设计上的问题,比如:
- 职责不清:两个bean互相依赖,可能意味着它们的职责划分不够清晰。
- 代码结构不合理:通过重构代码,将共同的功能提取到一个新的bean中,或者改变bean的注入方式(例如使用@Lazy注解延迟初始化),可以消除循环依赖。
建议:在设计Spring应用时,尽量保持bean的独立性,避免循环依赖。如果确实需要,考虑使用设计模式(如观察者模式、中介者模式)来重构代码。
五个容易引起别人阅读兴趣的标题:
- Spring循环依赖大揭秘:别让“相互等待”拖慢你的应用
- Java Spring新手必看:轻松搞定循环依赖问题
- Spring循环依赖?一篇文章带你走出困境
- Spring框架下的“死循环”之恋:循环依赖及其优雅解决方案
- 别让Spring循环依赖成为你的“拦路虎”