# 什么是 Spring IOC?
Table of Contents
什么是 Spring IOC?
Spring IOC 面试标准答案
【核心定义】
Spring IOC(控制反转)是一种设计模式,其核心思想是将对象的创建、依赖注入和生命周期管理从应用程序代码中转移到容器中,从而实现组件间的解耦。
【关键要点】
- 控制反转的本质:传统编程中对象自己控制依赖创建,IOC 将控制权交给容器,实现“好莱坞原则”(Don’t call us, we’ll call you)
- 依赖注入的实现方式:
- 构造器注入:通过构造函数传递依赖,保证对象创建时依赖就绪
- Setter 注入:通过 setter 方法设置依赖,更灵活但可能产生部分依赖状态
- 字段注入:通过 @Autowired 直接注入字段,简洁但有循环依赖风险
- IOC 容器的核心接口:
- BeanFactory:基础容器,提供基本的依赖注入功能
- ApplicationContext:高级容器,继承 BeanFactory,增加企业级功能(AOP、事件传播等)
【深度推导/细节】
容器初始化流程(逻辑拆解)
Step 1 - 配置加载 容器启动时读取 XML 配置或扫描注解,解析 Bean 定义(BeanDefinition)
Step 2 - Bean 实例化 根据 BeanDefinition 通过反射或 CGLIB 创建 Bean 实例
- 单例 Bean:容器启动时创建(默认作用域)
- 原型 Bean:每次请求时创建
Step 3 - 依赖注入 按照依赖关系图进行注入,解决循环依赖的关键机制:
- 三级缓存解决单例 Bean 的循环依赖
- 一级缓存:存放完全初始化好的 Bean
- 二级缓存:存放早期暴露的 Bean(仅实例化,未注入)
- 三级缓存:存放 Bean 工厂,用于创建代理对象
Step 4 - 初始化回调 执行初始化方法(@PostConstruct、InitializingBean、init-method)
Step 5 - Bean 就绪 Bean 进入可用状态,被放入单例池
循环依赖解决机制
// 以 A 依赖 B,B 依赖 A 为例1. 创建 A 实例(未注入) → 放入三级缓存2. 为 A 注入 B → 发现 B 不存在3. 创建 B 实例(未注入) → 放入三级缓存4. 为 B 注入 A → 从三级缓存获取 A 的早期引用5. B 完成注入 → 移入一级缓存6. A 完成 B 的注入 → 移入一级缓存【关联/对比】
Spring IOC vs 传统 new 对象
| 维度 | Spring IOC | 传统 new 对象 |
|---|---|---|
| 控制权 | 容器控制 | 程序员控制 |
| 耦合度 | 低耦合 | 高耦合 |
| 可测试性 | 易于 mock 测试 | 难以测试 |
| 灵活性 | 配置可动态变更 | 代码硬编码 |
BeanFactory vs ApplicationContext
- BeanFactory:轻量级,延迟加载,适合资源受限环境
- ApplicationContext:重量级,预加载所有单例 Bean,提供更多企业功能
- 国际化支持
- 事件发布机制
- AOP 集成
- 资源访问抽象
『面试官追问』
-
三级缓存具体如何工作?为什么需要三级而不是两级?
- 一级缓存:singletonObjects,完全初始化好的 Bean
- 二级缓存:earlySingletonObjects,早期 Bean(已实例化但未注入)
- 三级缓存:singletonFactories,Bean 工厂(用于处理 AOP 代理)
- 需要三级是因为 AOP 代理需要在注入前创建,工厂模式可以延迟代理创建
-
构造器注入和字段注入如何选择?
- 构造器注入:强制依赖,保证不可变,推荐使用
- Setter 注入:可选依赖,可重新配置
- 字段注入:简洁但隐藏依赖,不利于测试,Spring 4.3+ 不推荐
-
Bean 的作用域有哪些?
- singleton:默认,容器内单例
- prototype:每次请求新实例
- request:HTTP 请求生命周期
- session:HTTP 会话生命周期
- application:ServletContext 生命周期
- websocket:WebSocket 会话生命周期
-
@Autowired 和 @Resource 的区别?
- @Autowired:Spring 专属,按类型注入,可配合 @Qualifier 指定名称
- @Resource:JSR-250 标准,优先按名称注入,再按类型
-
如何解决多个同类型 Bean 的注入冲突?
- 使用 @Primary 指定主候选
- 使用 @Qualifier 指定具体 Bean 名称
- 使用 @Resource(name = “beanName”) 按名称注入
【版本差异】
Spring 2.x → 3.x → 4.x → 5.x 演进
- Spring 2.5:引入注解驱动开发(@Component, @Autowired)
- Spring 3.0:全面支持 Java 注解配置,引入 @Configuration
- Spring 4.0:支持 Java 8 特性,条件化 Bean 注册(@Conditional)
- Spring 4.3:构造器注入优化,单个构造器可省略 @Autowired
- Spring 5.0:响应式编程支持,Kotlin 扩展,性能优化
重要设计考量
-
延迟加载 vs 预加载
- BeanFactory 延迟加载:节省内存,启动快
- ApplicationContext 预加载:启动时发现问题,运行时性能好
-
循环依赖处理边界
- 仅支持单例 Bean 的 setter/字段注入循环依赖
- 不支持构造器注入的循环依赖(设计上应避免)
- 不支持原型 Bean 的循环依赖
-
性能优化点
- Bean 定义缓存:避免重复解析配置
- 单例池:快速获取已初始化 Bean
- 依赖解析缓存:加速依赖查找
回答要点总结:Spring IOC 通过容器管理对象生命周期和依赖关系,实现解耦。核心在于 BeanDefinition 的解析、三级缓存解决循环依赖、多种注入方式的选择。理解其底层机制有助于在复杂场景下正确使用和问题排查。