时效提示:本文基于 2026 年 4 月 Spring 生态最新状态编写,涵盖 Spring Framework 7.x 及 Spring Boot 4.x 技术体系。
Spring 框架在 2026 年的 Java 企业级开发中依然占据着不可撼动的统治地位。根据 Incus Data 发布的 2026 年 3 月 Java 生态报告,Spring 在 Java 企业系统中的使用率已达到约 75%,其中 Spring Boot 稳居现代 Java 后端开发的首选框架-46。在 2026 年的技术面试中,IoC(控制反转)与 DI(依赖注入)作为 Spring 框架的“灵魂双核”,几乎是绕不开的高频考点。

许多学习者对这两个概念的掌握长期停留在“会用注解”的层面。面试官一问“IoC 和 DI 的区别是什么?”“底层是如何实现的?”,不少人就开始含糊其词。本文将从痛点出发,帮你彻底理清 IoC 与 DI 的概念、关系、底层原理和面试考点,让 Spring 不再只是“会用”,而是“懂了”。
一、痛点切入:为什么需要 IoC 和 DI?

在传统 Java 开发中,对象之间的依赖关系通常由开发者手动管理。来看一个典型场景:
// 传统实现方式——硬编码依赖 public class OrderService { // 自己主动创建依赖对象 private PaymentService paymentService = new PaymentService(); private Logger logger = new FileLogger(); public void placeOrder(Order order) { logger.log("开始处理订单..."); paymentService.pay(order); } }
这种实现方式存在明显缺陷:
高度耦合:
OrderService直接依赖PaymentService和FileLogger的具体实现,更换支付方式或日志实现需要修改源代码;扩展性差:引入新的支付渠道(如微信支付、支付宝)需要大量改动;
难以测试:无法轻松替换为 Mock 对象进行单元测试;
代码冗余:每个需要日志记录的类都要重复创建
Logger对象。
Spring 的设计者正是看到了这些痛点,提出了 IoC 和 DI 的设计思想。
二、核心概念讲解:IoC(控制反转)
定义
IoC(Inversion of Control,控制反转) 是一种设计思想,它将原本由程序代码主动创建和管理对象的控制权,交给外部的 IoC 容器来统一管理-20。
拆解理解
“控制反转”的核心在于“反转”了什么呢?
传统方式:开发者手动
new对象 → 程序控制对象的生命周期IoC 方式:Spring 容器接管 → 容器控制对象的创建、装配和销毁
生活化类比
把 IoC 容器想象成一家“对象工厂”。你需要什么零件,直接告诉工厂,工厂按需生产并配送给你,而你不需要知道零件是怎么造出来的。在传统模式下,你需要自己开模、自己生产、自己组装——既麻烦又易错。
价值
IoC 带来的核心价值是解耦——将组件间的依赖关系从“硬编码”变为“声明式”,使系统更灵活、更易维护、更便于测试-20。
三、关联概念讲解:DI(依赖注入)
定义
DI(Dependency Injection,依赖注入) 是 IoC 的一种具体实现方式。Spring 容器在创建 Bean 时,自动将所依赖的其他 Bean“注入”到目标 Bean 中-。
DI 的三种实现形式
Spring 支持三种依赖注入方式:
| 注入方式 | 示例 | 适用场景 |
|---|---|---|
| 构造器注入(推荐) | @Autowired 放在构造器上 | 必需依赖、不可变依赖 |
| Setter 注入 | @Autowired 放在 Setter 方法上 | 可选依赖、可通过配置更改 |
| 字段注入(不推荐) | @Autowired 直接放在字段上 | 简单场景(但不利于测试) |
// 推荐:构造器注入(Spring 4.3+ 可省略 @Autowired) @Service public class OrderService { private final PaymentService paymentService; private final LoggerService loggerService; public OrderService(PaymentService paymentService, LoggerService loggerService) { this.paymentService = paymentService; this.loggerService = loggerService; } }
四、概念关系与区别总结
一句话概括:IoC 是“设计思想”,DI 是“落地手段”。
| 维度 | IoC(控制反转) | DI(依赖注入) |
|---|---|---|
| 性质 | 设计原则/思想 | 具体实现方式 |
| 关注点 | “谁来控制”——控制权从代码转移给容器 | “怎么控制”——通过注入方式传递依赖 |
| 实现方式 | 容器接管对象生命周期 | 构造器/Setter/字段注入 |
| 是否可独立存在 | 是,是一种通用设计思想 | 是 IoC 最常见的一种实现 |
IoC 主要的实现方式有两种:依赖查找(DL,Dependency Lookup)和依赖注入(DI)。Spring 采用的是 DI 方式,这也是最主流、最直观的实现-24。
五、代码示例:对比新旧实现方式
传统方式(无 Spring)
public class OldOrderService { // 手动创建所有依赖 → 高度耦合,难以测试 private DatabaseService db = new DatabaseService(); private EmailService email = new EmailService(); private LogService log = new LogService(); public void process(Order order) { db.save(order); email.send("订单已处理"); log.info("订单完成"); } }
Spring 方式(IoC + DI)
// 步骤1:配置类(或使用 @ComponentScan) @Configuration public class AppConfig { @Bean public DatabaseService databaseService() { return new DatabaseService(); } @Bean public EmailService emailService() { return new EmailService(); } } // 步骤2:业务类通过 DI 接收依赖 @Service public class OrderService { private final DatabaseService databaseService; private final EmailService emailService; // Spring 自动注入 @Autowired public OrderService(DatabaseService databaseService, EmailService emailService) { this.databaseService = databaseService; this.emailService = emailService; } public void process(Order order) { databaseService.save(order); // 无需手动创建 emailService.send("订单已处理"); } } // 步骤3:启动容器 public class Main { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); OrderService orderService = context.getBean(OrderService.class); orderService.process(new Order()); } }
关键改进:
OrderService不再主动new任何依赖,只需声明“我需要什么”依赖的具体实现可随时替换,无需修改业务代码
单元测试时可轻松注入 Mock 对象
六、底层原理 / 技术支撑
核心技术:反射 + 设计模式
IoC 容器的底层实现依赖两个核心支柱:
反射(Reflection) :Spring 容器在运行时动态加载类、获取构造器信息、调用方法、读写字段,从而实现“运行时创建对象并注入依赖”-24-30。
容器核心接口:
BeanFactory:最基础的 IoC 容器接口,定义
getBean()等核心方法,采用懒加载策略-30ApplicationContext:日常开发使用的增强版容器,继承
BeanFactory,支持非懒加载、国际化、事件发布等企业级特性-30-35
核心流程(三步走)
步骤 1:解析配置元数据 → 扫描 @Component、@Service 等注解,或解析 XML/JavaConfig,生成 BeanDefinition(Bean 的“说明书”)-30
步骤 2:注册到容器 → 将 BeanDefinition 存入注册表(本质是一个 Map<String, BeanDefinition>)-30
步骤 3:实例化与注入 → 容器根据 BeanDefinition,通过反射创建 Bean 实例,自动解析依赖关系并完成属性填充-30-35
💡 更深层次的原理将在后续的“AOP 原理深度解析”中展开讲解。
七、高频面试题与参考答案
Q1:说说你对 Spring IoC 的理解?
参考答案:
IoC 是 Inversion of Control(控制反转)的缩写,是一种设计思想。它将对象创建和依赖管理的控制权从程序代码转移给 Spring 的 IoC 容器。传统方式下,我们需要手动 new 对象;而在 IoC 模式下,开发者只需要声明依赖关系,容器负责创建对象、注入依赖并管理生命周期。这样做的好处是降低代码耦合度、提高可测试性和可维护性--。
Q2:IoC 和 DI 有什么区别?
参考答案:
IoC 是设计思想,DI 是具体实现方式。IoC 的核心是“反转控制权”,而 DI 是实现这种反转的手段——通过构造器注入、Setter 注入或字段注入,将依赖对象传递给被依赖的对象。一句话概括:IoC 是“思想”,DI 是“行动” --24。
Q3:Spring 中 BeanFactory 和 ApplicationContext 有什么区别?
参考答案:
BeanFactory 是 Spring 最基础的 IoC 容器接口,采用懒加载策略——只有调用 getBean() 时才创建 Bean,适合内存敏感的场景。ApplicationContext 继承了 BeanFactory,是功能更丰富的增强版容器,采用预加载策略(启动时创建所有单例 Bean),额外支持国际化、事件发布、资源加载等企业级功能,也是日常开发的首选-30。
Q4:Spring 中 Bean 的默认作用域是什么?还有哪些作用域?
参考答案:
默认作用域是 singleton(单例),即在整个容器中只有一个实例。其他作用域包括:prototype(每次获取都创建新实例)、request(每个 HTTP 请求对应一个实例)、session(每个 HTTP 会话对应一个实例)、application(每个 ServletContext 对应一个实例)-19。
Q5:Spring 如何解决循环依赖问题?
参考答案(进阶问题):
Spring 通过三级缓存机制解决单例 Bean 的循环依赖:
一级缓存(
singletonObjects):存放完全初始化好的单例 Bean二级缓存(
earlySingletonObjects):存放早期暴露的 Bean(尚未完成属性填充)三级缓存(
singletonFactories):存放 Bean 的工厂对象
当 A 依赖 B、B 依赖 A 时,Spring 在实例化 A 后,将 A 的早期引用存入二级缓存,然后注入 B;B 在创建过程中从二级缓存获取 A 的引用,从而完成循环。需要注意的是,构造器注入的循环依赖无法解决,会抛出 BeanCurrentlyInCreationException-35。
八、结尾总结
本文围绕 Spring 的两大核心概念 IoC 和 DI 展开,核心知识点梳理如下:
IoC(控制反转) :一种解耦的设计思想,将对象控制权从代码转移给容器
DI(依赖注入) :IoC 的具体实现方式,通过构造器/Setter/字段注入完成依赖传递
二者关系:IoC 是“指导思想”,DI 是“落地手段”
底层原理:容器 + BeanDefinition + 反射机制
面试要点:概念理解、两者区别、容器接口区别、作用域、循环依赖
🎯 一句话核心记忆:“IoC 是设计思想——不自己 new 对象,交给容器管;DI 是具体做法——告诉容器我需要什么,容器帮我把依赖送进来。”
易错点提醒:
不要将 IoC 和 DI 混为一谈,面试中区分二者是高频考点
构造器注入是官方推荐的方式,字段注入不利于测试
@Autowired默认按类型装配,@Resource默认按名称装配
后续预告:
下一篇将深入 Spring 的另一大核心——AOP(面向切面编程) ,讲解动态代理原理、@Transactional 失效原因以及 AOP 底层实现机制,敬请期待!
扫一扫微信交流