Spring 循环依赖:别让“相互等待”拖慢你的应用启动

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中,BeanABeanB都通过构造器注入了对方,形成了循环依赖。运行这个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的独立性,避免循环依赖。如果确实需要,考虑使用设计模式(如观察者模式、中介者模式)来重构代码。


五个容易引起别人阅读兴趣的标题:

  1. Spring循环依赖大揭秘:别让“相互等待”拖慢你的应用
  2. Java Spring新手必看:轻松搞定循环依赖问题
  3. Spring循环依赖?一篇文章带你走出困境
  4. Spring框架下的“死循环”之恋:循环依赖及其优雅解决方案
  5. 别让Spring循环依赖成为你的“拦路虎”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值