资讯动态

Spring IOC 容器原理详解:控制反转、依赖注入、Bean 生命周期

发布时间:2026/10/15 13:33:02 来源:尧图企业网站定制
IOC控制反转是 Spring 框架的基石SpringAOP、Spring 事务、SpringBoot 自动配置全部建立在 IOC 容器之上。很多后端开发者每天都在写Service、Autowired但对 IOC 底层运行机制、Bean 完整生命周期、循环依赖的底层解决方案理解很浅。本文从设计思想、核心接口、依赖注入方式、Bean 完整生命周期、循环依赖源码思路完整拆解 IOC 容器看完彻底吃透 Spring IOC。前言Spring 两大核心支柱IOC 容器与AOP 面向切面编程。 在没有 Spring 的年代我们创建对象需要手动new对象之间的依赖关系硬编码写死。如果要替换一个实现类需要修改多处代码耦合度极高单元测试也很难 Mock 依赖对象。IOC 思想的出现就是为了解决对象耦合问题把对象的创建、销毁、依赖装配、生命周期管理全部交给 Spring 容器。开发者不再主动去 new 对象控制权交给容器这就是控制反转 Inversion of Control。 而DI依赖注入是实现 IOC 思想的具体手段。总结IOC 是思想DI 是落地实现方式。一、IOC 核心概念控制反转 依赖注入 DI1.1 什么是控制反转IOC传统开发方式开发者主动控制。// 自己new对象自己维护依赖 UserService userService new UserService(); UserDao userDao new UserDao(); userService.setUserDao(userDao);对象创建、依赖装配的控制权掌握在程序员代码里。IOC 模式容器控制。 我们只需要告诉 Spring哪些类需要交给容器管理Component/Service/Repository/Controller由 Spring 容器扫描类、实例化对象、自动装配依赖开发者直接从容器获取 Bean 使用。 对象生命周期的控制权从开发者反转到 Spring 容器。1.2 依赖注入 DIDependency Injection容器自动把当前 Bean 所需要的依赖对象注入进来不需要手动实例化依赖。Spring 提供 3 种主流注入方式构造器注入Service public class UserService{ private final UserDao userDao; //构造器注入 public UserService(UserDao userDao){ this.userDao userDao; } }优点依赖不可变、保证依赖不为空方便单元测试Spring4.3 之后单构造器无需加Autowired。Setter 方法注入Autowired public void setUserDao(UserDao userDao){ this.userDao userDao; }适合依赖可选、运行时动态修改依赖场景。字段注入Autowired 直接写在成员变量上Autowired private UserDao userDao;写法简洁但缺点明显无法声明为 final、不能脱离 IOC 容器做单元测试不推荐生产项目大量使用。开发规范建议强制依赖使用构造器注入可选依赖使用 Setter 注入尽量避免字段注入。二、IOC 容器核心接口体系Spring IOC 容器顶层接口是BeanFactoryApplicationContext是 BeanFactory 的子接口也是我们开发中最常用的容器。BeanFactorySpring 最底层 IOC 容器懒加载 Bean只有调用getBean()的时候才会实例化对象。只提供基础 Bean 管理能力功能比较单薄。ApplicationContext应用上下文BeanFactory 的增强子接口我们项目实际使用的容器。启动时预实例化单例 Bean非懒加载容器启动就能提前发现 Bean 创建异常额外能力资源加载、国际化、事件发布机制、AOP 支持、环境变量解析 常见实现类ClassPathXmlApplicationContextxml 配置方式AnnotationConfigApplicationContext注解驱动SpringBoot 底层就是这个面试考点BeanFactory 和 ApplicationContext 区别 BeanFactory 懒加载ApplicationContext 启动时加载单例 Bean功能更丰富。ApplicationContext 继承 BeanFactory。三、BeanDefinitionBean 的元数据模型Spring 不会直接拿着 Class 类去创建对象而是先把被注解标记的类解析成BeanDefinition。 BeanDefinition 是 Bean 的 “描述模板”保存类的全部信息class 全限定名作用域singleton 单例 /prototype 原型懒加载标识初始化方法、销毁方法依赖信息流程扫描类 → 生成 BeanDefinition → 根据 BeanDefinition 实例化 Bean。 可以理解BeanDefinition 是图纸Bean 是根据图纸造出来的实例。四、Bean 完整生命周期完整生命周期分为实例化 → 属性填充 → 初始化 → 使用 → 销毁资源扫描与解析容器启动扫描指定包下带有Component等注解的类解析生成 BeanDefinition存入 BeanDefinitionMap。实例化 Instantiation通过 Java 反射机制调用构造方法new 出原始对象。此时仅仅是一块空对象成员变量还没有赋值。属性填充 PopulateBeanDI 依赖注入Spring 解析Autowired等注解从容器找到依赖 Bean给对象的成员变量赋值完成依赖注入。循环依赖就发生在这一步。Aware 接口回调感知容器如果 Bean 实现了一系列 Aware 接口容器回调方法让 Bean 拿到容器资源BeanNameAware拿到 Bean 在容器中的名字BeanFactoryAware拿到 BeanFactory 对象ApplicationContextAware拿到 ApplicationContext 上下文最常用BeanPostProcessor 前置处理Bean 后置处理器在初始化方法执行之前执行 BeanPostProcessor#postProcessBeforeInitialization。AOP 代理对象就是在这里判断并生成代理。初始化 Initialization按顺序执行 ①PostConstruct注解标记方法Java 规范 ② InitializingBean#afterPropertiesSet () 接口回调 ③ xml 配置的 init-method 自定义初始化方法BeanPostProcessor 后置处理执行 postProcessAfterInitialization。重点AOP 如果需要创建代理对象就在这一步完成。如果需要代理返回代理对象不需要代理返回原始对象。单例 Bean 放入单例池 singletonObjects初始化完成的完整 Bean存入ConcurrentHashMap单例池对外提供使用。singletonObjects 就是我们常说的一级缓存。Bean 使用阶段Bean 已经就绪业务代码可以直接注入、调用。销毁 Destruction容器关闭时执行销毁逻辑仅单例 Bean 会由容器管理销毁原型 Bean 容器不管理销毁 执行顺序 ①PreDestroy注解方法 ② DisposableBean#destroy () 接口回调 ③ xml 配置 destroy-method 自定义销毁方法记忆顺序实例化 → 填充属性 → Aware 回调 → 前置后置处理器 → PostConstruct → InitializingBean → init-method → 放入单例池使用 → 销毁。五、Bean 作用域详解Spring 支持多种 Bean 作用域面试常问前两个singleton默认单例。整个 IOC 容器只创建 1 个 Bean 实例存入单例池。容器启动创建非懒加载。prototype原型。每次调用getBean()都会新建一个对象容器不管理原型 Bean 的销毁。requestWeb 环境一次 http 请求一个实例sessionWeb 环境一个 session 一个实例applicationWeb 环境全局 ServletContext 级别单例坑点单例 Bean 中如果注入原型 Bean原型 Bean 不会每次都新建因为注入只发生一次需要通过 ApplicationContext 手动 getBean 获取原型实例。六、循环依赖底层原理6.1 什么是循环依赖A 类依赖 BB 类同时依赖 A。容器创建 A 的时候发现需要 B去创建 B创建 B 的时候发现需要 AA 还没完全创建完成。Component public class A{ Autowired private B b; } Component public class B{ Autowired private A a; }6.2 Spring 如何解决单例 setter 注入循环依赖三级缓存Spring 用三级缓存解决单例、setter 注入场景下的循环依赖//一级缓存存放完全初始化好可以直接使用的单例Bean private final MapString, Object singletonObjects new ConcurrentHashMap(); //二级缓存实例化完成但还没填充属性的早期Bean对象半成品Bean private final MapString, Object earlySingletonObjects new HashMap(); //三级缓存Bean工厂用来生成早期对象引用支持AOP代理 private final MapString, ObjectFactory? singletonFactories new HashMap();执行流程创建 A实例化得到原始对象开始填充属性发现依赖 B开始创建 B实例化原始对象填充属性发现依赖 AA 还没完成初始化但是 A 实例化已经完成把 A 的 ObjectFactory 放入三级缓存 singletonFactoriesB 去三级缓存拿到 A 的早期引用半成品 A完成 B 的属性填充、初始化B 完整创建完成放入一级缓存回到 A注入 B 对象A 完成属性填充初始化A 移入一级缓存删除二级、三级缓存中 A 的临时记录。6.3 不能解决循环依赖的场景原型 prototype 作用域 Bean无法解决原型 Bean 不使用三级缓存构造器注入的循环依赖无法解决实例化阶段就需要依赖对象还没机会放入三级缓存AOP 代理 循环依赖三级缓存的 ObjectFactory 就是用来生成代理对象这个场景可以支持。总结Spring 只能解决单例、setter / 字段注入场景循环依赖构造注入、原型 Bean 循环依赖直接抛出异常。七、IOC 容器带来的工程价值解耦对象之间不再硬编码依赖更换实现类不需要修改业务代码面向接口编程。统一管理 Bean 生命周期创建、初始化、销毁由容器统一管控。天然支持 AOP 增强IOC 把 Bean 统一托管才有机会生成代理对象实现事务、切面等能力。单元测试友好依赖可以轻松 Mock不需要手动 new 一整条依赖链。集中配置管理配合注解、配置文件统一管理对象创建逻辑。八、实战开发踩坑总结单例 Bean 是容器全局唯一不要在 Bean 中定义可变成员变量会出现线程安全问题原型 Bean 不要存到全局静态变量会导致原型失效PostConstruct是初始化方法构造器是实例化不要混淆构造器执行时依赖还没注入完成循环依赖优先改成设计层面解耦不要单纯依赖三级缓存兜底业务上循环依赖本身就是设计问题。九、小测Q1IOC 和 DI 的区别AIOC 是控制反转的设计思想把对象控制权交给容器DI 依赖注入是 IOC 的实现手段容器自动装配对象依赖。Q2BeanFactory 和 ApplicationContext 区别ABeanFactory 是底层接口懒加载 BeanApplicationContext 是子接口预加载单例 Bean提供国际化、事件发布等扩展能力开发中常用。Q3Bean 生命周期顺序PostConstruct 执行时机A实例化 → 属性填充 → Aware 回调 → BeanPostProcessor 前置处理 → PostConstruct → InitializingBean → init-method → 后置处理器 → 放入单例池使用。Q4三级缓存分别作用为什么需要三级两级行不行A一级完整 Bean二级半成品 Bean三级对象工厂用来生成 AOP 代理对象。如果没有三级缓存循环依赖场景下 AOP 代理无法生成两级缓存只能处理普通无代理 Bean。Q5哪些场景循环依赖 Spring 无法解决A原型 Bean、构造器注入产生的循环依赖。结语IOC 是 Spring 体系最底层的地基AOP、声明式事务、SpringBoot 自动配置全部构建在 IOC 容器之上。很多开发停留在会用Autowired但是理解 Bean 创建流程、三级缓存原理之后遇到线上 Bean 创建异常、循环依赖报错、AOP 失效问题才能快速定位根因。 掌握 IOC 之后再去学习 AOP理解难度会大幅降低。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑