Spring框架下的依赖注入坑:循环依赖与作用域混淆

本文介绍了Apollo自动驾驶开放平台如何助力汽车行业革新,包括云端仿真环境、DreamView的使用、离线数据包播放等关键步骤,旨在帮助开发者快速搭建自动驾驶系统。

前言

请添加图片描述

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家https://www.captainbed.cn/z
在这里插入图片描述
请添加图片描述


1. 错误场景复现

场景1:构造函数循环依赖

@Service
public class ServiceA {
    private final ServiceB serviceB;
    public ServiceA(ServiceB serviceB) { // 构造函数注入
        this.serviceB = serviceB;
    }
}

@Service
public class ServiceB {
    private final ServiceA serviceA;
    public ServiceB(ServiceA serviceA) { // 构造函数注入
        this.serviceA = serviceA;
    }
}

后果:应用启动时抛出BeanCurrentlyInCreationException,提示循环依赖。


场景2:单例Bean中的原型Bean失效

@Scope("prototype")
@Service
public class PrototypeBean {
    private int count = 0;
    public void add() { count++; }
    public int getCount() { return count; }
}

@Service
public class SingletonBean {
    @Autowired
    private PrototypeBean prototypeBean; // 单例中注入原型Bean

    public void increment() {
        prototypeBean.add();
        System.out.println(prototypeBean.getCount()); // 每次输出递增,而非期望的1
    }
}

问题:单例Bean只初始化一次,导致注入的原型Bean实例被复用,失去“原型”特性。


场景3:多级循环依赖(A→B→C→A)

@Service
public class ServiceA {
    @Autowired private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired private ServiceC serviceC;
}

@Service
public class ServiceC {
    @Autowired private ServiceA serviceA; // 三级循环依赖
}

现象:Spring可能正常启动,但某些情况下(如AOP代理)仍会触发循环依赖异常。


2. 原理解析

Spring解决循环依赖的三级缓存

缓存层级存储内容作用阶段
一级缓存(singletonObjects)完全初始化的单例Bean应用运行期
二级缓存(earlySingletonObjects)提前暴露的未完成初始化的Bean(仅对象引用)Bean创建中
三级缓存(singletonFactories)Bean工厂,用于生成提前暴露的BeanBean实例化后、属性填充前
  • 构造函数注入无法解决循环依赖:Bean实例化前需先获取依赖对象,而构造函数注入必须在实例化时完成依赖注入,导致死锁。
  • Setter注入/字段注入可解决:实例化后通过反射注入依赖。

原型Bean在单例中的失效原因

  • 单例Bean初始化仅一次:依赖的原型Bean在初始化时注入,后续方法调用复用同一实例。
  • 解决方案:通过ApplicationContext实时获取,或使用@Lookup注解。

3. 正确解决方案

方案1:打破循环依赖

方法1.1:代码重构(优先推荐)
// 提取公共逻辑到第三个类
@Service
public class CommonService { /* 公共方法 */ }

@Service
public class ServiceA {
    private final CommonService commonService;
    public ServiceA(CommonService commonService) { ... }
}

@Service
public class ServiceB {
    private final CommonService commonService;
    public ServiceB(CommonService commonService) { ... }
}
方法1.2:使用@Lazy延迟加载
@Service
public class ServiceA {
    public ServiceA(@Lazy ServiceB serviceB) { // 延迟注入代理对象
        this.serviceB = serviceB;
    }
}

注意@Lazy可能导致调试困难,需谨慎使用。


方案2:原型Bean的正确使用

方法2.1:ApplicationContext实时获取
@Service
public class SingletonBean {
    @Autowired
    private ApplicationContext context;

    public void increment() {
        PrototypeBean prototypeBean = context.getBean(PrototypeBean.class);
        prototypeBean.add();
        System.out.println(prototypeBean.getCount()); // 每次输出1
    }
}
方法2.2:使用ObjectFactory或Provider
@Service
public class SingletonBean {
    @Autowired
    private ObjectFactory<PrototypeBean> prototypeFactory;

    public void increment() {
        PrototypeBean prototypeBean = prototypeFactory.getObject();
        prototypeBean.add();
        System.out.println(prototypeBean.getCount());
    }
}
方法2.3:@Scope注解设置代理模式
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Service
public class PrototypeBean { ... }

// 单例Bean直接注入
@Service
public class SingletonBean {
    @Autowired 
    private PrototypeBean prototypeBean; // 每次调用时动态生成新实例
}

4. 工具与最佳实践

循环依赖检测工具

  1. Spring Actuator
    启用beans端点,查看Bean依赖关系:

    management:
      endpoints:
        web:
          exposure:
            include: beans
    

    访问:http://localhost:8080/actuator/beans

  2. IDE插件

    • IntelliJ的Spring插件可视化Bean依赖图
    • Eclipse的Spring Tools Suite

最佳实践

  • 优先使用构造函数注入:强制依赖明确,避免NullPointerException
  • 避免循环依赖:是设计问题而非技术问题,重构模块职责
  • 谨慎使用原型作用域:仅在需要状态隔离时使用(如用户会话)

5. Code Review检查清单

检查项正确做法
是否存在构造函数循环依赖?优先重构代码,其次使用@Lazy
原型Bean是否在单例中失效?检查是否使用ObjectFactory或@Scope代理
是否滥用字段注入?优先选择构造函数注入或Setter注入
是否误用@Primary注解?确保不会掩盖其他Bean的正确注入

6. 真实案例

某微服务项目因循环依赖导致启动失败:

  • 背景:订单服务(OrderService)与库存服务(StockService)相互构造函数注入。
  • 错误日志Requested bean is currently in creation: Is there an unresolvable circular reference?
  • 修复步骤
    1. 提取公共验证逻辑到独立模块(ValidationService)
    2. 将部分方法改为Setter注入
    3. 添加单元测试验证启动流程

结果:启动时间减少30%,模块职责更清晰。


总结

  • 循环依赖是设计警钟:重构优于技术规避
  • 作用域需精确控制:单例与原型混用要慎防状态污染
  • 注入方式影响扩展性:构造函数注入促进不可变性与清晰依赖
  • 工具辅助事半功倍:利用Spring生态工具快速定位问题

下期预告:《技术债累积警告:复制粘贴代码的连锁反应》——从代码重复率到维护成本,揭秘如何用设计模式和工具重构腐化代码。

联系作者

职场经验分享,Java面试,简历修改,求职辅导尽在科技泡泡
思维导图面试视频合集
在这里插入图片描述
在这里插入图片描述

内容概要:本文针对传统三电平并网逆变器在谐波抑制、电网不平衡适应性及动态响应方面的不足,提出一种基于有源中点箝位(ANPC)三电平拓扑的高性能并网控制策略。该策略深度融合双极性倍频脉宽调制(DPWMA)、正负序分离锁相技术电网电压前馈控制,构建了“精准同步—扰动补偿—优质调制”三位一体的一体化控制体系。依托ANPC拓扑在开关损耗均衡、中点电位稳定和低谐波输出方面的硬件优势,结合DPWMA调制提升等效开关频率、正负序分离实现不平衡电网下的精确锁相、前馈控制克服闭环滞后等先进控制手段,显著改善了系统的稳态电能质量、动态响应速度复杂工况适应能力。通过多工况仿真验证,该复合策略在稳态运行时可大幅降低总谐波畸变率,在电网不平衡动态扰动工况下仍能维持并网电流对称、功率平稳及快速恢复能力,展现出优异的综合性能工程应用潜力。; 适合人群:具备电力电子电力系统基础知识,从事新能源并网、逆变器控制、微电网或相关领域研究的研发人员及研究生。; 使用场景及目标:① 提升高功率并网逆变器的电能质量运行稳定性;② 解决电网电压不平衡、畸变等复杂工况下的并网难题;③ 优化动态响应性能,提升系统抗扰能力;④ 为ANPC拓扑先进控制策略的工程化应用提供技术参考。; 阅读建议:建议结合仿真模型深入理解DPWMA调制、正负序分离锁相前馈控制的实现细节,重点关注多工况下的性能对比分析,以掌握复合控制策略的设计逻辑优化效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪碧有白泡泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值