# 什么是 Spring IOC?

7 min read
Table of Contents

什么是 Spring IOC?

Spring IOC 面试标准答案

【核心定义】

Spring IOC(控制反转)是一种设计模式,其核心思想是将对象的创建、依赖注入和生命周期管理从应用程序代码中转移到容器中,从而实现组件间的解耦。

【关键要点】

  1. 控制反转的本质:传统编程中对象自己控制依赖创建,IOC 将控制权交给容器,实现“好莱坞原则”(Don’t call us, we’ll call you)
  2. 依赖注入的实现方式:
    • 构造器注入:通过构造函数传递依赖,保证对象创建时依赖就绪
    • Setter 注入:通过 setter 方法设置依赖,更灵活但可能产生部分依赖状态
    • 字段注入:通过 @Autowired 直接注入字段,简洁但有循环依赖风险
  3. 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 集成
    • 资源访问抽象

『面试官追问』

  1. 三级缓存具体如何工作?为什么需要三级而不是两级?

    • 一级缓存:singletonObjects,完全初始化好的 Bean
    • 二级缓存:earlySingletonObjects,早期 Bean(已实例化但未注入)
    • 三级缓存:singletonFactories,Bean 工厂(用于处理 AOP 代理)
    • 需要三级是因为 AOP 代理需要在注入前创建,工厂模式可以延迟代理创建
  2. 构造器注入和字段注入如何选择?

    • 构造器注入:强制依赖,保证不可变,推荐使用
    • Setter 注入:可选依赖,可重新配置
    • 字段注入:简洁但隐藏依赖,不利于测试,Spring 4.3+ 不推荐
  3. Bean 的作用域有哪些?

    • singleton:默认,容器内单例
    • prototype:每次请求新实例
    • request:HTTP 请求生命周期
    • session:HTTP 会话生命周期
    • application:ServletContext 生命周期
    • websocket:WebSocket 会话生命周期
  4. @Autowired 和 @Resource 的区别?

    • @Autowired:Spring 专属,按类型注入,可配合 @Qualifier 指定名称
    • @Resource:JSR-250 标准,优先按名称注入,再按类型
  5. 如何解决多个同类型 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 扩展,性能优化

重要设计考量

  1. 延迟加载 vs 预加载

    • BeanFactory 延迟加载:节省内存,启动快
    • ApplicationContext 预加载:启动时发现问题,运行时性能好
  2. 循环依赖处理边界

    • 仅支持单例 Bean 的 setter/字段注入循环依赖
    • 不支持构造器注入的循环依赖(设计上应避免)
    • 不支持原型 Bean 的循环依赖
  3. 性能优化点

    • Bean 定义缓存:避免重复解析配置
    • 单例池:快速获取已初始化 Bean
    • 依赖解析缓存:加速依赖查找

回答要点总结:Spring IOC 通过容器管理对象生命周期和依赖关系,实现解耦。核心在于 BeanDefinition 的解析、三级缓存解决循环依赖、多种注入方式的选择。理解其底层机制有助于在复杂场景下正确使用和问题排查。

My avatar

文章看完了,不打算去别的篇章转转吗?👀 感谢你的耐心阅读!如果你有问题想探讨,或者发现了文中的 Bug,请务必通过页脚的链接联系我


More Posts

# 什么是 Spring Boot?

4 min read

【核心定义】Spring Boot 是一个基于 Spring 框架的快速开发脚手架,通过约定大于配置和自动装配机制,简化了 Spring 应用的初始搭建和开发过程。

Read

Comments