IoC/DI 思想与容器职责
结论先行:IoC(控制反转)是把"对象创建 + 依赖装配"的控制权从代码自身反转到容器;DI(依赖注入)是容器实现 IoC 的主要手段——容器创建 Bean 时把它的依赖"注入"进来,调用方只声明依赖、不负责 new。Spring IoC 容器(BeanFactory/ApplicationContext)的职责可归纳为一条流水线:解析 Bean 定义 → 实例化 → 依赖注入 → 生命周期管理 →(按需)AOP 代理织入与事件发布。
一、IoC 与 DI 的关系
- IoC 是思想/原则:谁控制谁——原来对象自己 new 依赖(正转),现在容器统一创建并组装(反转);
- DI 是落地方式:容器通过构造器、setter 或字段把依赖传给对象;
- 反过来控制反转还有其他形式(如 Service Locator),但 Spring 主推 DI,面试时先说"Spring 的 IoC 通过 DI 实现"。
二、三种注入方式对比
| 方式 | 写法 | 优点 | 缺点 |
|---|---|---|---|
| 构造器注入 | 构造器参数接收 | 依赖不可变(final)、易测试、必填依赖显式 | 依赖多时代码长;构造器循环依赖不可解 |
| Setter 注入 | setter 方法 | 可选依赖、可后期重配 | 依赖可变、可能遗漏注入 |
| 字段注入 | @Autowired 直接标字段 | 代码最简 | 隐藏依赖、难单测、与容器强耦合 |
- 官方推荐:构造器注入(Spring 团队文档明确建议);Boot 中也最常见;
- 字段注入在面试中常被问"为什么不推荐":依赖关系不可见、无法脱离容器做单元测试。
三、容器职责链
- 读取配置/扫描注解,解析出 BeanDefinition(bean 的类型、作用域、依赖、初始化方法等);
- 按需实例化对象(默认单例并预实例化,见 03);
- 依赖注入(populateBean,@Autowired/@Value 在此阶段生效);
- 生命周期管理:Aware 回调、初始化、销毁回调(见 04);
- 扩展能力:BeanPostProcessor 链(AOP 代理在此产生)、事件发布。
@Service
public class OrderService {
private final PaymentGateway gateway; // 只声明依赖,不 new
public OrderService(PaymentGateway gateway) { // 构造器注入:容器负责传参
this.gateway = gateway;
}
}
常见追问 / 记忆点
- 追问:IoC 容器里 Bean 什么时候被创建?答:默认单例 Bean 在 ApplicationContext 启动刷新时预实例化(eager),prototype 与懒加载 Bean 在首次 getBean/被注入时创建。
- 追问:IoC 最大的好处?答:解耦(替换实现只改配置/注解)、便于单元测试(可注入 Mock)、对象统一管理。
- 记忆点:"反转的是创建权,注入的是依赖,容器是总装配车间"。