容器是怎么管理 Bean 的?
文章目录
- 1. 核心蓝图:BeanDefinition
- 2. 核心管理流程:生命周期流水线
- 第一阶段:准备与实例化
- 第二阶段:装配与填充
- 第三阶段:初始化(Initialization)
- 第四阶段:生存与销毁
- 3. 核心机制:三级缓存(解决循环依赖)
- 4. BeanFactory vs ApplicationContext
- 总结
Spring 容器对 Bean 的管理不仅仅是“创建对象”那么简单,它实际上是一套极其严密的生命周期流水线。
容器通过BeanFactory(生产工厂)和BeanDefinition(施工蓝图)这两个核心组件,完成了从读取配置到销毁对象的所有工作。
1. 核心蓝图:BeanDefinition
在容器真正 new 一个对象之前,它首先要把你的配置(XML、注解、Starter)转化成一个统一的内部结构:BeanDefinition。
这个蓝图记录了:
- Bean 的全类名(Class Name)。
- 作用域(Scope):单例还是多例。
- 是否懒加载(Lazy-init)。
- 依赖关系:它需要注入哪些别的 Bean。
- 初始化和销毁方法名。
2. 核心管理流程:生命周期流水线
你可以把容器想象成一个自动化工厂,每一个 Bean 都要走完以下流程:
第一阶段:准备与实例化
- 加载配置:容器启动,读取扫描路径下的所有类。
- 注册 BeanDefinition:将信息存入
BeanDefinitionMap。 - 实例化(Instantiation):通过反射机制,调用构造函数创建一个“原生对象”。此时它只是个空的 Java 对象,还没注入数据。
第二阶段:装配与填充
- 属性赋值(Populate):容器根据蓝图,把对应的配置参数和依赖的其他 Bean 注入进去(比如
@Value、@Autowired)。 - Aware 回调:如果 Bean 实现了
BeanNameAware或ApplicationContextAware,容器会把 Bean 的名字或容器引用塞给它。
第三阶段:初始化(Initialization)
- BeanPostProcessor 前置处理:执行所有拦截器的
postProcessBeforeInitialization方法。 - 初始化方法:先后执行
@PostConstruct、InitializingBean接口方法、以及自定义的init-method。 - BeanPostProcessor 后置处理:执行
postProcessAfterInitialization。注意:AOP(动态代理)通常就在这一步发生。
第四阶段:生存与销毁
- 就绪:Bean 进入单例池(Singleton Objects Map),等待被业务代码调用。
- 销毁(Destruction):当容器关闭时,执行销毁逻辑(
@PreDestroy等)。
3. 核心机制:三级缓存(解决循环依赖)
管理中最难的部分是:如果 A 依赖 B,B 也依赖 A 怎么办?
容器通过三级缓存(本质是三个 Map)来解决这个问题:
- 一级缓存(singletonObjects):存放完全初始化好的、可以直接用的“成品”Bean。
- 二级缓存(earlySingletonObjects):存放已经实例化、但还没填充属性的“半成品”Bean。
- 三级缓存(singletonFactories):存放创建 Bean 的工厂对象,用来处理 AOP 情况下的循环依赖。
逻辑:A 创建时发现依赖 B,先把自己这个“半成品”挂在缓存里,跑去创建 B;B 发现依赖 A,直接从缓存里拿走 A 的“半成品”完成创建,再回头把完整的 B 给 A。
4. BeanFactory vs ApplicationContext
容器的管理能力也分等级:
| 特性 | BeanFactory | ApplicationContext |
|---|---|---|
| 定位 | 底层接口,提供基本容器功能。 | 高级容器,继承了前者。 |
| 加载时机 | 懒加载。只有获取 Bean 时才创建。 | 预加载。容器启动时就创建所有单例。 |
| 扩展功能 | 无。 | 支持 AOP、国际化、事件广播、资源访问。 |
| 应用场景 | 移动端或资源极度受限环境(现已少用)。 | 几乎所有 Spring/Spring Boot 应用。 |
总结
容器管理 Bean 的本质是:利用BeanDefinition统一标准,利用“反射 + 缓存”实现自动化组装,利用“生命周期接口”提供可扩展性。
你现在已经看过了 Bean 的创建、销毁、以及容器的管理逻辑。你是否好奇过,为什么 Spring 默认要把 Bean 设计成单例(Singleton),这种设计在处理高并发请求时会有什么潜在的“坑”?
