一文看懂Spring IoC与DI:AI助手文本搜索资料带你扫清盲区

小编头像

小编

管理员

发布于:2026年04月29日

3 阅读 · 0 评论

2026年4月10日 · 北京

在Spring技术生态中,IoC(控制反转)和DI(依赖注入)是所有进阶学习的基石。然而大量开发者长期处于“会用注解、能跑项目,但一问原理就卡壳”的状态。本文将依托AI助手文本资料,从痛点出发,带你看清这两个核心概念的本来面目,彻底终结“面试答不出、原理讲不清”的困境。

一、痛点切入:传统开发为何失控

在传统的Java开发中,对象间的依赖关系通常由开发者手动完成:

java
复制
下载
public class OrderService {
    // 硬编码依赖 —— 想换实现?改代码重编译!
    private PaymentService payment = new AlipayService();
    private Logger logger = new FileLogger("/tmp/log");
    
    public void pay() {
        payment.process();
        logger.log("支付完成");
    }
}

这种“new地狱”暴露的三大致命缺陷

  1. 紧耦合 —— OrderServiceAlipayService 死死绑定,想换成微信支付必须修改源代码-3

  2. 难以测试 —— 单元测试时无法用Mock对象替换真实依赖,只能拉整个完整依赖链-50

  3. 职责混乱 —— 业务类既要处理核心逻辑,又要负责创建依赖对象,严重违背单一职责原则-9

更棘手的是,一个对象背后往往链接着多层依赖:要拿A,得先new B,B又依赖C和D……工作量迅速失控,代码就像缠成一团的蜘蛛网-3

二、控制反转(IoC):把“new”的权力上交

定义与内涵

控制反转(Inversion of Control,简称IoC) 是一种设计原则,它的核心思想是:将对象的创建和依赖管理的控制权,从应用程序代码转移到外部容器-9

用一句话理解:传统开发中,是 “我”主动new对象(控制正转);IoC模式下,是 “容器”帮你创建和管理对象(控制反转)。Spring官方称之为“好莱坞原则”——Don‘t call us, we’ll call you(别找我们,我们会找你)-3

反转了什么

IoC把三类控制权从开发者手中反转给了容器:

控制项传统开发(开发者控制)IoC模式(容器控制)
对象创建手动new容器根据配置自动创建
依赖组装手动set容器自动注入
生命周期管理依赖JVM回收容器管理初始化/销毁

-8

三、依赖注入(DI):IoC落地的具体手段

定义

依赖注入(Dependency Injection,简称DI) 是一种设计模式,是IoC的具体实现方式。容器在运行时动态地将依赖关系注入到目标对象中,而不是由目标对象自己创建依赖-9

核心问答框架

  • 谁负责创建依赖? → 容器(Spring IoC容器)

  • 谁决定依赖关系? → 配置(注解/XML/Java Config)

  • 对象如何获取依赖? → 被动接收(构造函数、Setter或字段注入)-3

三种注入方式对比

① 构造器注入(Constructor Injection)—— 最推荐!

java
复制
下载
@Service
public class OrderService {
    private final PaymentService paymentService;  // final保证不可变
    
    // Spring Boot 2.6+ 只有一个构造器时,@Autowired可省略
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

✅ 优点:依赖不可变、避免空指针、便于单元测试、大厂标配

② Setter注入(Setter Injection)

java
复制
下载
@Service
public class OrderService {
    private PaymentService paymentService;
    
    @Autowired
    public void setPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

✅ 灵活,适合可选依赖;⚠️ 依赖可变,可能为null

③ 字段注入(Field Injection)—— 慎用

java
复制
下载
@Service
public class OrderService {
    @Autowired
    private PaymentService paymentService;  // 最简洁,但隐患最多
}

⚠️ 无法注入final、可被反射改值、不利于测试-19

四、IoC与DI的关系:一句话总结

IoC是思想,DI是实现。

IoC定义了“把控制权交给容器”这个目标和方向;DI则回答了“容器到底怎么做”这个问题——通过构造器、Setter或注解,把依赖“塞”进去-8

对比维度IoC(控制反转)DI(依赖注入)
本质设计思想/原则设计模式/实现方式
回答的问题为什么要把控制权交出去?具体怎么交?
核心表述反转了对象的创建控制权容器自动注入依赖

五、底层原理:反射 + 容器 + Bean生命周期

IoC容器的底层实现依赖两大支柱:

  1. Java反射机制:容器通过反射动态调用构造函数创建对象、通过反射获取字段和方法进行属性注入-29

  2. 容器体系:核心接口是BeanFactory(最基础的IoC能力),日常开发用的是功能更丰富的ApplicationContext,后者继承了BeanFactory并增加了国际化、事件发布等能力-29

Bean的完整生命周期大致如下:

实例化 → 属性填充(依赖注入) → Aware接口回调 → BeanPostProcessor前置处理 → 初始化 → BeanPostProcessor后置处理 → 使用 → 销毁-27

这里特别留意:AOP代理对象是在BeanPostProcessor后置处理阶段生成的,这也是面试中常见的追问点。

进阶考点:循环依赖与三级缓存

当A依赖B、B又依赖A时,就形成了循环依赖。Spring通过三级缓存来优雅解决单例Bean的循环依赖问题:

缓存级别缓存名称作用
一级缓存singletonObjects存放完全初始化的成品Bean
二级缓存earlySingletonObjects存放提前暴露的半成品Bean(已实例化未填充)
三级缓存singletonFactories存放ObjectFactory工厂,按需生成代理对象

三级缓存的设计精妙之处在于:将“要不要生成代理”的判断延迟到第一次被其他Bean引用时才计算,既解决了循环依赖,又保证了AOP代理的正常生效-37-38

六、高频面试题与参考答案

Q1:什么是IoC?什么是DI?它们有什么关系?

参考答案

  • IoC(控制反转) 是一种设计思想,将对象的创建和依赖管理的控制权从应用程序代码转移到外部容器。

  • DI(依赖注入) 是IoC的具体实现方式,由容器在运行时动态地将依赖注入到目标对象中。

  • 两者是 “思想与实现”的关系:IoC指明了“做什么”,DI回答了“怎么做”。

Q2:Spring中有哪几种依赖注入方式?各有什么优缺点?

方式优点缺点推荐度
构造器注入依赖不可变、避免空指针、便于测试参数多时构造器过长★★★★★
Setter注入灵活、支持可选依赖依赖可变、可能为null★★
字段注入代码最简洁无法注入final、可被反射改值★★★★

一句话记住:强制依赖用构造器,可选依赖用Setter,字段注入日常用但慎用-19

Q3:Spring如何解决循环依赖?为什么需要三级缓存?

参考答案

  • Spring通过三级缓存解决单例Bean的setter/字段注入循环依赖。

  • 一级缓存singletonObjects存成品,二级缓存earlySingletonObjects存半成品,三级缓存singletonFactoriesObjectFactory工厂。

  • 需要三级而非二级的原因:三级缓存将代理生成的决定权延迟到真正被引用时才执行,既解决了循环依赖,又保证了AOP代理在合适的时机生效-37-38

⚠️ 注意:构造器注入无法解决循环依赖,需要使用@Lazy注解来规避-1

Q4:@Autowired和@Resource有什么区别?

对比项@Autowired@Resource
来源Spring框架自带JDK标准(JSR-250)
默认注入策略按类型(byType)按名称(byName)
当匹配到多个Bean配合@Qualifier指定指定name属性
适用场景同类型单候选时最简洁需要明确按名称匹配时

-2-18

七、结尾总结

回顾全文,需要重点掌握的知识点:

  1. IoC是思想,DI是实现 —— 这是整个Spring容器的核心哲学

  2. 三种注入方式 —— 构造器注入是大厂标配,掌握区别与适用场景

  3. 三级缓存机制 —— 面试中循环依赖问题的标准答案,理解每级缓存的职责

  4. @Autowired vs @Resource —— 记住来源和默认注入策略即可应对绝大多数考题

⚠️ 易错提醒:很多人误以为“IoC就是DI”,其实二者是“目标”与“手段”的关系。另外,Spring默认Bean是单例,在多线程场景下需要注意线程安全问题-1

📌 下一篇预告:本文将进入Bean生命周期源码解析,深入拆解refresh()方法的12个核心步骤,敬请期待。


参考资料:本文综合参考了阿里云开发者社区、CSDN、腾讯云社区等多篇2025-2026年相关技术文章

标签:

相关阅读