Spring 启动流程详解与时序图
在面试中,Spring 的启动流程是一个高频问题。本文将通过时序图和详细分析,带你深入理解 Spring 容器(以 AnnotationConfigApplicationContext 为例)的启动过程,并解答一些常见疑惑。
时序图
以下是 Spring 启动流程的时序图(使用 Mermaid 语法,可在 Mermaid Live Editor 中渲染):
sequenceDiagram
participant U as 用户
participant AC as AnnotationConfigApplicationContext
participant BF as DefaultListableBeanFactory
participant BFP as BeanFactoryPostProcessor
participant BPP as BeanPostProcessor
participant EM as ApplicationEventMulticaster
participant EL as ApplicationListener
participant BEAN as Bean实例
U->>AC: new AnnotationConfigApplicationContext(AppConfig)
AC->>AC: refresh()
AC->>AC: prepareRefresh()
note right of AC: 初始化环境、属性源、验证属性
AC->>BF: obtainFreshBeanFactory()
BF-->>AC: 返回BeanFactory(含BeanDefinition)
note right of BF: 扫描@Component、注册Bean定义
AC->>BF: prepareBeanFactory()
note right of BF: 配置类加载器、添加Aware处理器、忽略接口
AC->>BFP: invokeBeanFactoryPostProcessors()
BFP-->>BF: 修改BeanDefinition(如@Configuration)
note right of BFP: 动态调整Bean定义
AC->>BPP: registerBeanPostProcessors()
BPP-->>AC: 注册完成(按@Order排序)
note right of BPP: 支持AOP、依赖注入
AC->>AC: initMessageSource()
AC->>EM: initApplicationEventMulticaster()
EM-->>AC: 返回事件多播器
AC->>AC: onRefresh()
note right of AC: 子类扩展(如Spring Boot启动Web容器)
AC->>EM: registerListeners()
EM->>EL: 添加ApplicationListener
note right of EL: 监听ContextRefreshedEvent等
AC->>BF: finishBeanFactoryInitialization()
BF->>BEAN: preInstantiateSingletons()
BEAN-->>BF: 实例化、依赖注入、初始化(三级缓存、Aware、AOP)
note right of BEAN: 处理循环依赖、@PostConstruct
AC->>EM: finishRefresh()
EM->>EL: publishEvent(ContextRefreshedEvent)
note right of EM: 通知容器就绪
U-->>AC: 容器启动完成
Spring 启动流程详解
1. 容器初始化:创建 ApplicationContext
用户通过 new AnnotationConfigApplicationContext(AppConfig.class) 创建容器,触发启动流程。
2. 为什么要 refresh()?它的深意是什么?
- 调用:
refresh()是AbstractApplicationContext的核心方法,启动容器的入口。 - 深意:
- Spring 设计之初,
BeanFactory是一个轻量级容器,只负责 Bean 的创建和管理。但ApplicationContext作为高级容器,增加了事件机制、国际化等功能。 refresh()的作用是“刷新”容器状态,将所有组件(Bean 定义、处理器、事件系统等)初始化并整合为一个完整的运行时环境。- 它是一个“一次性启动”的过程,确保容器从无到有,状态一致,避免手动调用多个方法带来的复杂性。
- Spring 设计之初,
- 类比:就像电脑开机,
refresh()是按下电源键后系统自检和加载的全过程。
3. 创建并配置 BeanFactory
- obtainFreshBeanFactory():
- 创建
DefaultListableBeanFactory,扫描@ComponentScan路径,注册BeanDefinition。
- 创建
- prepareBeanFactory():
- 配置类加载器、添加
BeanPostProcessor(如ApplicationContextAwareProcessor)。 - 忽略接口的疑惑:
- Spring 会忽略
Aware接口(如BeanNameAware、BeanFactoryAware、ApplicationContextAware)的自动注入。 - 原因:这些接口不是通过
@Autowired注入,而是由特定的BeanPostProcessor在 Bean 初始化时手动设置。例如,ApplicationContextAwareProcessor会调用setApplicationContext()。 - 深意:避免开发者误用自动注入导致混乱,同时保持 Aware 机制的控制权在框架手中。
- Spring 会忽略
- 配置类加载器、添加
4. BeanFactoryPostProcessor
- 调用
invokeBeanFactoryPostProcessors(),如ConfigurationClassPostProcessor,解析@Configuration和@Bean,动态调整BeanDefinition。
5. BeanPostProcessor 是什么?
- 调用:
registerBeanPostProcessors()注册所有BeanPostProcessor。 - 定义:
BeanPostProcessor是一个接口,允许在 Bean 实例化后、初始化前后介入,执行自定义逻辑。 - 例子:
- AOP:
AbstractAutoProxyCreator在初始化后生成代理对象。 - Aware:设置
BeanNameAware的值。
- AOP:
- @Order 的作用:多个
BeanPostProcessor可通过@Order或Ordered接口排序,确保执行顺序。例如,AOP 代理需要在属性填充后执行。 - 与 BFP 的区别:
BeanFactoryPostProcessor修改BeanDefinition,而BeanPostProcessor操作已创建的 Bean 实例。
6. onRefresh() 的作用
- 调用:
onRefresh()是refresh()中的一个钩子方法。 - 作用:提供给子类扩展,例如 Spring Boot 在此启动嵌入式 Web 容器(如 Tomcat)。
- 与 refresh() 的区别:
refresh()是整个启动流程的总控方法,onRefresh()是其中的一个步骤。- 它不是“重新刷新”,而是“特定场景下的初始化”,名称上容易误解。
- 深意:体现了 Spring 的可扩展性,允许子类在标准流程中插入自定义逻辑。
7. 注册事件监听器:registerListeners()
- 调用:将
ApplicationListener注册到ApplicationEventMulticaster。 - 事件是什么?:
- Spring 的事件基于观察者模式,
ApplicationEvent是事件实体,ApplicationListener是监听器。 - 例子:
ContextRefreshedEvent表示容器刷新完成。
- Spring 的事件基于观察者模式,
- 如何理解:
- 类比于“消息队列”,事件发布后,监听器异步处理。
- 代码实体:
ApplicationEventMulticaster#multicastEvent()负责分发。
- 作用:解耦业务逻辑,例如容器启动后通知某些组件执行初始化。
8. 实例化单例 Bean:finishBeanFactoryInitialization()
- 调用:
beanFactory.preInstantiateSingletons()。 - 详细过程:
- 实例化:通过构造方法创建对象。
- 三级缓存解决循环依赖:
- 一级缓存:单例池,存完全初始化的 Bean。
- 二级缓存:早期暴露的对象(未初始化)。
- 三级缓存:工厂对象,用于生成代理(如 AOP)。
- 流程:A 依赖 B,B 未完成时,A 从二级缓存获取 B 的早期引用。
- 属性填充:执行依赖注入。
- 初始化:
- 调用
Aware接口方法(如setBeanName)。 - 执行
@PostConstruct(通过CommonAnnotationBeanPostProcessor)。 - 调用
init-method或InitializingBean.afterPropertiesSet()。
- 调用
- AOP 代理:由
BeanPostProcessor生成代理对象。
- 分析:这是 Spring 核心功能(DI 和 AOP)的实现阶段。
9. 完成刷新:finishRefresh()
- 调用:发布
ContextRefreshedEvent。 - ContextRefreshedEvent 是什么?:
- 一个具体的事件类,继承自
ApplicationEvent,表示容器已就绪。 - 代码实体:
new ContextRefreshedEvent(this),由ApplicationEventMulticaster分发。
- 一个具体的事件类,继承自
- 作用:通知监听器容器启动完成,可能触发后续逻辑。
总结
Spring 的 refresh() 流程是一个从无到有的构建过程,通过 BeanFactory、BeanPostProcessor 和事件机制,实现了高度灵活的容器管理。理解这些细节,不仅能应对面试,还能帮助你更好地使用和扩展 Spring。