北京时间:2026年4月8日 16:32:19
一、开篇引入

对于每一位 Java 后端开发者而言,Spring 几乎是绕不开的必修课。无论是企业级应用开发,还是微服务架构落地,Spring 都是当之无愧的“基石框架”。据统计,超过 80% 的 Spring 核心模块直接或间接依赖 IoC 容器提供的服务-22。
很多开发者长期处于“会用但不懂原理”的状态:@Autowired 天天写,可它背后到底发生了什么?IoC 和 DI 到底是不是一回事?面试官一问就卡壳。

本文作为 Spring 原理系列的第一篇,将从零开始,系统讲解 IoC(控制反转) 与 DI(依赖注入) 的核心概念、二者关系、代码示例、底层原理,并附上高频面试题与标准答案,帮助你在理解原理的同时,从容应对面试。
二、痛点切入:为什么需要 IoC 与 DI?
先看一段典型的传统 Java 代码。
假设我们要“造一辆车”:汽车依赖车身,车身依赖底盘,底盘依赖轮胎。传统开发中,所有依赖都是通过 new 手动创建的:
public class Main { public static void main(String[] args) { Car car = new Car(21); car.run(); } } public class Tire { int size; public Tire(Integer size) { this.size = size; System.out.println("tire init, size: " + size); } } public class Bottom { private Tire tire; public Bottom(Integer size) { this.tire = new Tire(size); System.out.println("bottom init..."); } } public class Framework { private Bottom bottom; public Framework(Integer size) { this.bottom = new Bottom(size); System.out.println("framework init..."); } } public class Car { private Framework framework; public Car(Integer size) { this.framework = new Framework(size); System.out.println("car init..."); } public void run() { System.out.println("car run..."); } }
运行后控制台输出依次为:tire init, size: 21 → bottom init... → framework init... → car init... → car run...-1。
这段代码存在两个致命问题:
耦合度过高:当最底层的轮胎尺寸需要修改时,整个调用链(Car → Framework → Bottom → Tire)上的所有代码都要改,改一处牵动全身-1。
扩展性极差:如果需要更换轮胎的实现类(比如从普通轮胎换成防爆轮胎),必须修改 Bottom 类的内部代码,违反开闭原则。
IoC 的出现,就是为了解决这个“高耦合”的难题。
三、核心概念讲解:IoC(控制反转)
标准定义:
英文全称:Inversion of Control
中文释义:控制反转
核心内涵:将对象的创建权和依赖关系的管理权,从程序代码本身反转给外部容器--28。
一句话理解: 传统模式下,你主动 new 对象;IoC 模式下,你只管“要”,容器帮你“造”。
生活化类比——订外卖:
传统模式:你想吃一顿饭,得自己买菜、洗菜、切菜、炒菜,从头到尾亲力亲为-1。
IoC 模式:你打开外卖 App 下单,餐厅(容器)负责采购、烹饪、打包,你只需要“接收”成品-34。
IoC 的核心价值:
解耦:对象与对象的创建逻辑分离,代码不再相互绑定
可测试性提升:可轻松注入 Mock 对象进行单元测试-22
可维护性增强:依赖变更时只需修改配置,无需改动业务代码
四、关联概念讲解:DI(依赖注入)
标准定义:
英文全称:Dependency Injection
中文释义:依赖注入
核心内涵:容器在创建对象时,自动将该对象所依赖的其他对象“送过去”,开发者无需手动关联依赖关系--2。
DI 的三种实现方式:
| 注入方式 | 实现机制 | 适用场景 | 推荐度 |
|---|---|---|---|
| 构造器注入 | 通过构造函数传递依赖 | 依赖是必需的、不可变的 | ⭐⭐⭐⭐⭐(官方推荐) |
| Setter 注入 | 通过 setter 方法注入依赖 | 依赖可选,或允许运行时变更 | ⭐⭐⭐ |
| 字段注入 | 通过 @Autowired 直接注入字段 | 简化代码,但可测试性较差 | ⭐⭐ |
// 构造器注入(推荐) @Component public class UserService { private final UserDao userDao; public UserService(UserDao userDao) { this.userDao = userDao; } } // Setter 注入 @Component public class UserService { private UserDao userDao; @Autowired public void setUserDao(UserDao userDao) { this.userDao = userDao; } } // 字段注入(简单但不推荐) @Component public class UserService { @Autowired private UserDao userDao; }
五、概念关系与区别总结
一句话概括:IoC 是一种设计思想,DI 是实现 IoC 的具体手段-49。
| 对比维度 | IoC(控制反转) | DI(依赖注入) |
|---|---|---|
| 性质 | 设计思想 / 设计原则 | 具体实现技术 |
| 关注点 | 控制权从谁手中转移到谁手中 | 如何将依赖传递进来 |
| 回答的问题 | “谁来创建对象?” | “依赖怎么给进来?” |
| 地位 | 宏观指导思想 | 微观落地手段 |
💡 记忆口诀:IoC 是“思想”,DI 是“做法”;IoC 告诉你“让容器管”,DI 告诉你“容器怎么管”。
六、代码示例:IoC + DI 实战对比
现在用 Spring 的方式重写上面的“造车”代码,直观感受区别:
// 轮胎——被依赖的组件 @Component public class Tire { private int size; @Value("18") public void setSize(int size) { this.size = size; } } // 底盘——依赖轮胎 @Component public class Bottom { private Tire tire; @Autowired public void setTire(Tire tire) { this.tire = tire; } } // 车身——依赖底盘 @Component public class Framework { private Bottom bottom; @Autowired public void setBottom(Bottom bottom) { this.bottom = bottom; } } // 汽车——依赖车身 @Component public class Car { private Framework framework; @Autowired public void setFramework(Framework framework) { this.framework = framework; } public void run() { System.out.println("car run..."); } } // 测试类——从容器中获取对象,无需手动管理依赖 public class Main { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); Car car = context.getBean(Car.class); car.run(); } }
关键变化解读:
没有任何
new关键字,所有对象由容器创建使用
@Component标注需要被容器管理的类使用
@Autowired声明依赖关系,容器自动装配-3底层依赖反射机制:Spring 通过
Class.forName()获取类的字节码,再调用Constructor.newInstance()动态创建对象-21;通过Field.setAccessible(true)访问私有字段注入依赖-21。
七、底层原理与技术支撑
IoC 和 DI 之所以能“神奇地”自动创建对象、自动注入依赖,核心依赖的是 Java 反射机制。
反射在 Spring 中的关键应用:
对象创建:Spring 扫描
@Component、@Service等注解标注的类后,通过Class.forName()获取类信息,再调用Constructor.newInstance()动态创建实例,全程无需new-21。依赖注入:当检测到
@Autowired标注的字段时,Spring 通过Field.setAccessible(true)突破私有访问限制,直接注入依赖对象-21。注解解析:通过
Class.getAnnotations()获取类、方法、字段上的注解信息,进而执行相应的逻辑-21。AOP 代理:代理对象在调用目标方法时,通过
Method.invoke()反射执行原方法,并在前后插入增强逻辑-21。
💡 面试金句:反射是 Spring Boot 框架的“灵魂”,没有反射,就无法实现 IoC、DI、AOP 等核心功能-21。
设计模式层面:Spring IoC 容器本质上是 工厂模式 + 反射机制 的组合实现-——工厂负责创建对象,反射提供“万能创建能力”。
关于反射性能:虽然反射存在一定性能损耗,但 Spring 通过缓存、懒加载、ASM 字节码操作等优化手段,将其影响降到最低,成为 Java 生态中最成功的框架之一-21。
八、高频面试题与参考答案
面试题1:说说你对 IoC 的理解?
参考答案:IoC 全称 Inversion of Control,即控制反转,是一种设计思想。它将对象的创建权和依赖管理权从程序代码本身反转给外部容器(Spring IoC 容器)。传统模式下需要开发者手动 new 对象,IoC 模式下开发者只需声明“我需要什么”,容器负责创建对象、管理生命周期并自动装配依赖,从而降低代码耦合度,提升可测试性和可维护性-。
踩分点:全称 + 定义 + 传统 vs IoC 对比 + 三个好处
面试题2:IoC 和 DI 有什么区别?它们的关系是什么?
参考答案:IoC 是一种设计思想,而 DI 是实现 IoC 的具体技术手段。IoC 回答的是“控制权交给谁”的问题,DI 回答的是“依赖怎么给进来”的问题。在 Spring 中,DI 通过构造器注入、Setter 注入和字段注入三种方式,实现了 IoC 思想中“容器自动装配依赖”的落地--。
踩分点:思想 vs 手段 + 一句话总结 + 三种注入方式
面试题3:Spring 中的 Bean 作用域有哪些?默认是什么?
参考答案:Spring 支持五种作用域。singleton(默认):容器中只有一个实例;prototype:每次获取都创建新实例;request:每个 HTTP 请求创建一个实例(仅 Web 环境);session:每个 HTTP Session 一个实例(仅 Web 环境);global-session:全局 Session 一个实例(Portlet 环境)--31。
踩分点:五种名称 + 默认是 singleton
面试题4:Spring 中的单例 Bean 是线程安全的吗?
参考答案:Spring 框架本身没有对单例 Bean 进行多线程封装处理,因此单例 Bean 不一定是线程安全的。但实际开发中,如果 Bean 中不定义有状态的共享变量(如实例变量),而是将数据放在方法局部变量中(线程私有),则可以认为是安全的-31。
踩分点:不是自动安全 + 取决于有无状态
面试题5:Spring IoC 容器的底层实现原理是什么?
参考答案:Spring IoC 容器的底层原理是 工厂模式 + Java 反射机制。容器启动时,通过解析配置(XML/注解)生成 BeanDefinition(Bean 的“说明书”),注册到容器中;当需要创建 Bean 时,容器通过反射调用构造器动态创建对象,再通过反射完成依赖注入(如 @Autowired 字段的赋值)。核心接口包括 BeanFactory(基础容器)和 ApplicationContext(增强版容器)--28。
踩分点:工厂模式 + 反射 + BeanDefinition + BeanFactory/ApplicationContext
九、结尾总结
本文核心知识点回顾:
| 知识点 | 核心内容 |
|---|---|
| 传统开发的痛点 | 高耦合、难维护、扩展性差 |
| IoC(控制反转) | 设计思想,将对象创建权反转给容器 |
| DI(依赖注入) | 实现手段,容器自动注入依赖对象 |
| 二者关系 | IoC 是思想,DI 是做法 |
| 底层原理 | 工厂模式 + Java 反射机制 |
| Bean 作用域 | 默认 singleton,五种可选 |
| 线程安全 | 单例 Bean 不一定安全,取决于是否有状态 |
需要特别记住的要点:
⚠️ 不要把 IoC 和 DI 混为一谈——IoC 是思想,DI 是手段
⚠️ 反射是 Spring 的“灵魂”,面试中被问到 IoC 原理时,提到反射是加分项
下一篇预告: 将深入讲解 Bean 的完整生命周期(实例化 → 属性赋值 → 初始化 → 销毁),以及 循环依赖的解决原理(三级缓存),敬请期待!
📌 本文是 Spring 原理系列的第一篇,后续将陆续更新 Bean 生命周期、循环依赖、AOP 原理等深度内容,欢迎持续关注。