电子展会
HOME
电子展会
正文内容
一文搞懂 Spring IoC 与 DI:领航 AI 助手带你梳理考点与原理
发布时间 : 2026-04-29
作者 : 小编
访问数量 : 19
扫码分享至微信

北京时间 2026年4月9日 | 阅读约 10 分钟

在 Spring 生态中,控制反转(IoC)依赖注入(DI) 是任何一位开发者都绕不开的核心基石。面试时被问到“说一下你对 Spring 的理解”,十有八九要从它们讲起;工作中遇到的很多代码设计问题,归根结底也在它们身上。许多学习者的真实困境是:会配 @Autowired,却说不清 IoC 和 DI 究竟是什么关系;知道能解耦,却讲不出底层是怎么实现的。

这篇文章正是为了解决这个痛点。我们将从痛点切入 → 概念拆解 → 关系梳理 → 代码演示 → 底层原理 → 面试考点,一步步帮你建立起关于 Spring IoC 与 DI 的完整知识链路。


一、痛点切入:为什么需要 IoC 和 DI?

先看一段“传统写法”的代码:

java
复制
下载
public class UserController {
    private UserService userService;
    
    public UserController() {
        // 直接在构造方法中 new 出依赖对象
        this.userService = new UserService();
    }
    
    public void handleRequest() {
        userService.doBusiness();
    }
}

public class UserService {
    private UserDao userDao;
    
    public UserService() {
        this.userDao = new UserDao();
    }
}

这段代码的问题在哪?

  • 高耦合UserControllerUserService 的实现类绑死在一起,改不了也测不了。

  • 扩展性差:想替换成 UserServiceV2,得改 UserController 的源码。

  • 难以测试:想用 Mock 对象替代真实的 UserService 做单元测试?做不到,因为对象在内部写死了。

这就是“控制正转”——对象主动去创建自己需要的依赖,自己掌握控制权。当系统规模变大、依赖关系变复杂时,这种方式会让代码变成一团乱麻。

为了解决这个问题,IoC 容器应运而生:把“创建对象”和“管理依赖”的权力从开发者手中转移到容器中。你只需要声明“我需要什么”,容器会在合适的时机把依赖给你送过来。


二、核心概念讲解:控制反转(IoC)

标准定义

IoC(Inversion of Control,控制反转) 是一种设计原则(programming principle),而非具体技术-。它的核心思想是:将对象的创建、配置和生命周期管理交给容器,而非由程序代码直接控制-

拆解关键词

  • “控制” :指的是对象的创建权、依赖关系的管理权、生命周期的控制权。

  • “反转” :控制权从应用程序代码反转到外部容器。在传统模式下,A 需要 B 就自己 new B();在 IoC 模式下,A 不再自己创建 B,而是由容器把 B 送过来。

生活化类比:从“自由恋爱”到“父母之命”

想象一个场景:在现代社会,小明自由恋爱,自己找对象、自己追、自己处(对象由自己创建和控制)——这就是控制正转。有一天小明穿越到了古代,婚姻大事由父母和媒人全权操办,他只需要等着“被安排”(对象由外部容器管理)——这就是控制反转-31

核心价值

IoC 让代码从“主动控制”变为“被动接收”,从而实现了组件间的解耦。对象之间不再直接持有强引用,耦合度显著降低,可测试性大幅提升-13


三、关联概念讲解:依赖注入(DI)

标准定义

DI(Dependency Injection,依赖注入) 是 IoC 的具体实现方式。容器在创建 Bean 的过程中,自动将依赖的 Bean 注入到目标 Bean 中-15。用一句话概括:对象不自己找依赖,而是被动接收别人给它的依赖-4

DI 的三种主流方式

1. 构造器注入(推荐)

java
复制
下载
@Service
public class UserService {
    private final UserDao userDao;
    
    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;   // 依赖通过构造方法传入
    }
}

2. Setter 注入

java
复制
下载
@Service
public class OrderService {
    private PaymentGateway gateway;
    
    @Autowired
    public void setGateway(PaymentGateway gateway) {
        this.gateway = gateway;   // 依赖通过 setter 传入
    }
}

3. 字段注入(最常用但最不推荐)

java
复制
下载
@Service
public class ProductService {
    @Autowired
    private InventoryService inventory;   // 直接注入字段
}

为什么构造器注入被推荐?

维度构造器注入字段注入
不可变性✅ 依赖可声明为 final❌ 无法声明 final
空安全✅ 对象创建时依赖必须就绪❌ 可能出现依赖未注入的情况
测试友好✅ 无需容器即可测试❌ 依赖 Mock 框架或容器
循环依赖检测✅ 启动时即可发现❌ 可能运行时才发现
代码可读性✅ 依赖一目了然❌ 依赖藏在字段中

-3


四、概念关系与区别总结

一句话总结:IoC 是思想,DI 是手段。

  • IoC 是设计原则(What) :告诉你“控制权要反转出去”。

  • DI 是技术实现(How) :告诉你“如何把控制权反转出去——通过注入的方式”。

对比表格

维度IoC(控制反转)DI(依赖注入)
本质设计思想 / 原则具体实现 / 技术模式
关注点谁控制谁怎么把依赖送进去
可替代性理论上可以用 DL(依赖查找)实现DI 是 Spring 中最常用的实现方式
层级宏观微观

-3-

💡 面试必备金句:IoC 是 Spring 框架的设计思想,DI 是 Spring 实现 IoC 的核心手段。两者相辅相成,共同构成了 Spring 容器的基础。


五、代码示例:从混乱到优雅

传统方式 vs Spring 方式对比

传统方式:硬编码依赖

java
复制
下载
public class OrderController {
    private OrderService orderService;
    
    public OrderController() {
        // 硬编码依赖,难以替换
        this.orderService = new OrderServiceImpl();
    }
}

Spring 方式:声明式依赖

java
复制
下载
@RestController
public class OrderController {
    
    @Autowired
    private OrderService orderService;   // Spring 自动注入
    
    @PostMapping("/order")
    public Result createOrder(@RequestBody OrderDTO order) {
        return orderService.create(order);
    }
}

完整可运行示例:三层架构依赖注入

java
复制
下载
// 1. DAO 层
@Repository
public class OrderDao {
    public void save(Order order) {
        System.out.println("订单已保存: " + order.getId());
    }
}

// 2. Service 层
@Service
public class OrderService {
    @Autowired
    private OrderDao orderDao;   // Spring 自动注入 OrderDao
    
    public void createOrder(Order order) {
        // 业务逻辑处理...
        orderDao.save(order);
    }
}

// 3. Controller 层
@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;   // Spring 自动注入 OrderService
    
    @PostMapping("/order")
    public String create(@RequestBody Order order) {
        orderService.createOrder(order);
        return "success";
    }
}

执行流程:Spring 容器启动时,扫描所有带有 @Component@Service@Repository@RestController 注解的类,将它们封装为 BeanDefinition,然后依次实例化并完成依赖注入。最终,OrderController 拿到的是已经装配好 OrderService 的完整对象,OrderService 内部也已装配好 OrderDao-15

@Autowired 工作流程

当 Spring 遇到 @Autowired 时,执行以下步骤:

  1. 根据需要的组件类型,到 IoC 容器中查找匹配的 Bean-23

  2. 唯一匹配:直接注入,完成✔;

  3. 找不到匹配:装配失败,抛出异常❌;

  4. 找到多个匹配:进入冲突解决机制——若有 @Qualifier,按指定名称匹配;若没有,按成员变量名作为 Bean ID 进行匹配-23


六、底层原理 / 技术支撑

Spring IoC 容器的底层实现,核心可以概括为四个字:工厂模式 + 反射

1. 工厂模式

IoC 容器本质上就是一个超级对象工厂BeanFactory 是 Spring 中最基础的 IoC 容器接口,定义了 getBean()containsBean() 等核心方法。日常开发中我们通常使用它的增强版——ApplicationContext-15

2. 反射机制

反射是 IoC 容器实现“运行时动态创建对象”的技术基础。Spring 在实例化 Bean 时,通过反射调用类的构造方法创建实例;在属性填充时,通过反射调用 setter 方法或直接访问字段来注入依赖-15-

3. 核心执行流程

容器启动到 Bean 就绪,大致经历三个阶段:

  • 加载配置元数据:解析 XML、注解或 Java Config,将扫描到的类封装为 BeanDefinition(Bean 的“说明书”)。

  • 注册 BeanDefinition:将 BeanDefinition 存入注册表(本质是一个 Map<String, BeanDefinition>)。

  • 实例化与依赖注入:根据 BeanDefinition,通过反射创建对象,再通过反射完成依赖注入-15

4. 三级缓存(解决循环依赖)

当两个 Bean 相互依赖时(A 依赖 B,B 依赖 A),Spring 通过三级缓存机制解决:

  • 一级缓存(singletonObjects) :存放完全初始化好的单例 Bean。

  • 二级缓存(earlySingletonObjects) :存放半成品 Bean(仅实例化,未完成属性填充)。

  • 三级缓存(singletonFactories) :存放 ObjectFactory,用于提前暴露 Bean 引用。

注意:构造器注入的循环依赖无法解决,这是构造器注入的“缺点”之一,也是为什么需要谨慎设计依赖关系-40


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

Q1:IoC 和 DI 的区别是什么?

踩分点:思想 vs 实现 + 二者关系 + 一句话总结

IoC(控制反转)是一种设计思想,它将对象的创建和依赖管理权从应用程序代码转移到外部容器。DI(依赖注入)是 IoC 的具体实现方式,指容器在创建对象时将依赖自动注入。两者是“思想与手段”的关系:IoC 是 What,DI 是 How。一句话:IoC 是设计原则,DI 是实现技术,Spring 通过 DI 实现了 IoC 容器。-3-

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

踩分点:三种方式 + 推荐构造器注入的原因

三种方式:

  1. 构造器注入:依赖通过构造方法传入,推荐使用。优点是不可变性(final)、测试友好、空安全、便于检测循环依赖。

  2. Setter 注入:依赖通过 setter 方法传入,适合可选依赖或需要动态修改的场景。

  3. 字段注入:直接 @Autowired 字段,代码最简洁,但导致代码与 Spring 容器耦合、无法声明 final、不利于测试,不推荐在生产代码中使用-3

Q3:Spring 如何解决循环依赖?

踩分点:三级缓存机制 + 适用条件 + 不能解决的场景

Spring 通过三级缓存解决 setter 注入和字段注入下的单例 Bean 循环依赖:

  • 一级缓存 singletonObjects:完全初始化好的 Bean。

  • 二级缓存 earlySingletonObjects:半成品 Bean。

  • 三级缓存 singletonFactories:提前暴露 Bean 的引用。

当 A 依赖 B、B 依赖 A 时,Spring 先将 A 的 ObjectFactory 存入三级缓存,在 B 创建完成后,A 能从缓存中获取到 B 的引用,从而完成装配。构造器注入的循环依赖无法解决-40-13

Q4:BeanFactory 和 ApplicationContext 有什么区别?

踩分点:初始化时机 + 功能扩展 + 使用场景

维度BeanFactoryApplicationContext
初始化时机懒加载(首次 getBean() 时才创建)容器启动时立即初始化所有单例 Bean
功能仅基础 IoC 能力扩展了国际化、事件发布、资源加载、AOP 支持等
使用场景轻量级场景或资源受限环境企业级开发(日常首选)

ApplicationContext 是 BeanFactory 的子接口,日常开发中通常使用 AnnotationConfigApplicationContextClassPathXmlApplicationContext-15-8


八、结尾总结

回顾全文,我们一起梳理了:

知识点核心要点
✅ 痛点传统代码高耦合、难测试、扩展性差
✅ IoC设计思想——控制权从代码反转到容器
✅ DI实现手段——依赖通过构造器/Setter/字段注入
✅ 关系IoC 是 What,DI 是 How,Spring 用 DI 实现 IoC
✅ 代码@Autowired 工作流程:按类型 → 唯一/多个/冲突解决
✅ 原理工厂模式 + 反射 + 三级缓存
✅ 面试IoC vs DI、三种注入方式、循环依赖、BeanFactory vs ApplicationContext

易错点提醒:不要把 IoC 和 DI 当成同一个概念去背答案,面试官很在意你能不能讲清“思想”与“实现”的区别。另外,字段注入虽然方便,但建议在正式项目中优先使用构造器注入。


预告

下一篇文章我们将深入 Spring AOP(面向切面编程) ,从 JDK 动态代理和 CGLIB 的区别讲起,带你搞懂 @Transactional 失效的原因、切面执行顺序以及自定义注解切面的实战技巧。欢迎持续关注!


文中相关完整代码示例已整理成项目,关注【领航 AI 助手】回复“SpringIoC”即可获取。


📌 本文关键词:Spring IoC · 依赖注入 · 控制反转 · 面试题 · 底层原理 · 构造器注入 · 循环依赖 · 三级缓存

王经理: 180-0000-0000(微信同号)
10086@qq.com
北京海淀区西三旗街道国际大厦08A座
©2026  上海羊羽卓进出口贸易有限公司  版权所有.All Rights Reserved.  |  程序由Z-BlogPHP强力驱动
网站首页
电话咨询
微信号

QQ

在线咨询真诚为您提供专业解答服务

热线

188-0000-0000
专属服务热线

微信

二维码扫一扫微信交流
顶部