Spring ApplicationContext 初始化流程
本文聚焦 Spring 容器初始化的核心 refresh() 方法。它讲解了从加载 Bean 定义、执行后置处理器,到最终实例化所有 Bean,直至容器准备就绪的全过程,揭示了 Spring 启动的内幕。
这是一个非常核心且重要的问题。理解 Spring ApplicationContext 的初始化流程对于深入掌握 Spring 框架至关重要。
我会用一个 “开一家餐厅” 的比喻来贯穿整个流程,并结合关键的源码方法(主要是 refresh() 方法)来详细解释。
核心比喻
ApplicationContext:餐厅的总经理,负责整个餐厅的开业和运营。- 配置文件 (XML/JavaConfig):餐厅的菜单和菜谱(定义了有哪些菜,以及怎么做)。
BeanDefinition:一道菜的具体菜谱,包含了菜名、原料、制作步骤等信息。BeanFactory:餐厅的后厨,真正负责制作菜品的地方。BeanFactoryPostProcessor:开业前的厨房顾问,可以在大厨开始做菜前,统一修改所有菜谱(比如,老板说今天所有菜都少放点盐)。Bean:根据菜谱做出来的、可以端上桌的成品菜。BeanPostProcessor:每一道菜出锅后的质检员/装饰师,可以在菜品端上桌前进行最后的加工(比如,给牛排撒上黑胡椒、给蛋糕插上装饰)。
ApplicationContext 初始化核心入口:refresh() 方法
无论你是用 new ClassPathXmlApplicationContext("beans.xml") 还是 new AnnotationConfigApplicationContext(AppConfig.class),它们的构造函数内部都会调用一个核心的 refresh() 方法。这个方法标志着容器初始化流程的正式开始。
整个流程可以分为以下几个关键阶段:
详细流程分解(refresh() 方法的13个步骤)
下面我们按照 refresh() 方法的执行顺序,拆解为几个主要阶段和关键步骤。
阶段一:准备阶段 (餐厅开业前的准备工作)
prepareRefresh():- 作用:设置容器的启动时间,激活状态标志,初始化属性源(property sources)。
- 比喻:总经理记录下餐厅的开业时间,挂上“正在准备”的牌子,并检查一下今天的原材料供应情况。
obtainFreshBeanFactory():- 作用:创建或获取一个全新的
BeanFactory(通常是DefaultListableBeanFactory),并加载所有的BeanDefinition。 - 对于 XML 配置:此时会创建
XmlBeanDefinitionReader来解析 XML 文件,将<bean>标签转换成一个个BeanDefinition对象,然后注册到BeanFactory中。 - 对于注解配置:此时会创建
AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner来扫描指定的包,找到带有@Component,@Service,@Configuration等注解的类,将它们也转换成BeanDefinition注册到BeanFactory中。 - 比喻:总经理建立了一个全新的后厨 (
BeanFactory),并把菜单 (<bean>或@Component) 上的所有菜谱 (BeanDefinition) 都整理好,放进了后厨的菜谱架上。 - 关键点:此时,只有菜谱 (
BeanDefinition),还没有任何一道真正的菜 (Bean实例) 被做出来。
- 作用:创建或获取一个全新的
prepareBeanFactory(beanFactory):- 作用:对
BeanFactory进行一系列的配置。比如设置类加载器(ClassLoader)、添加一些默认的BeanPostProcessor(例如ApplicationContextAwareProcessor,它能让 Bean 获取到ApplicationContext实例)。 - 比喻:总经理给后厨配置好基本的厨具(类加载器),并告诉厨师们一些基本规则(比如如何处理特殊需求的订单)。
- 作用:对
postProcessBeanFactory(beanFactory):- 作用:一个模板方法,留给子类扩展。例如,
WebApplicationContext会在这里添加对ServletContext的支持。 - 比喻:如果这家餐厅是连锁店(子类),那么在这里会进行一些该分店特有的准备工作。
- 作用:一个模板方法,留给子类扩展。例如,
阶段二:应用后置处理器 (顾问和质检员入场)
invokeBeanFactoryPostProcessors(beanFactory):- 作用:这是一个非常重要的步骤! Spring 会在这里找到所有实现了
BeanFactoryPostProcessor接口的 Bean,并依次执行它们。 - 这些处理器可以读取、修改所有
BeanDefinition。最典型的例子是PropertySourcesPlaceholderConfigurer,它会扫描BeanDefinition中的${...}占位符,并用配置文件(如application.properties)中的值替换它们。 - 比喻:厨房顾问 (
BeanFactoryPostProcessor) 入场,他会检查所有的菜谱 (BeanDefinition)。发现菜谱里写着“盐(适量)”,顾问会根据老板的要求,把它们都改成“盐(5克)”。 - 关键点:它操作的是 Bean 的定义 (菜谱),而不是 Bean 的实例 (成品菜)。
- 作用:这是一个非常重要的步骤! Spring 会在这里找到所有实现了
registerBeanPostProcessors(beanFactory):- 作用:找到所有实现了
BeanPostProcessor接口的 Bean,并将它们注册到BeanFactory的拦截器链中。 - 注意:这里只是“注册”,并不会立即执行。它们会在后续 Bean 实例化的过程中被回调。
- 常见的
BeanPostProcessor有:AutowiredAnnotationBeanPostProcessor(处理@Autowired注解)、CommonAnnotationBeanPostProcessor(处理@PostConstruct,@PreDestroy注解)、以及 AOP 相关的处理器,它们负责创建代理对象。 - 比喻:总经理把所有的质检员/装饰师 (
BeanPostProcessor) 都请到后厨,让他们站好位置,准备对后面出锅的每一道菜进行检查和装饰。
- 作用:找到所有实现了
阶段三:初始化内部组件
initMessageSource(): 初始化国际化(i18n)消息源。initApplicationEventMulticaster(): 初始化事件广播器,用于支持 Spring 的事件驱动模型 (ApplicationEvent)。onRefresh(): 另一个模板方法,留给子类初始化其他特殊的 Bean。例如,Spring MVC 的DispatcherServlet会在这里初始化它的 Web 组件。
阶段四:实例化 Bean (真正开始做菜)
registerListeners(): 将配置文件中定义的ApplicationListener注册到事件广播器中。finishBeanFactoryInitialization(beanFactory):- 作用:核心中的核心! 实例化所有剩余的、非懒加载的单例(Singleton)Bean。
- 这个过程会遍历
BeanFactory中所有的BeanDefinition。 - 对于每一个需要创建的 Bean,会触发
getBean()->doGetBean()的调用链。 - Bean 的生命周期在这里完整体现:
a. 实例化 (Instantiation):通过反射创建 Bean 的实例对象。
b. 属性填充 (Populate):处理@Autowired等注解,进行依赖注入。
c. 初始化 (Initialization):- 调用各种
Aware接口(如BeanNameAware,ApplicationContextAware)。 - 执行
BeanPostProcessor的postProcessBeforeInitialization方法。 - 调用
@PostConstruct注解的方法或InitializingBean接口的afterPropertiesSet方法。 - 执行
BeanPostProcessor的postProcessAfterInitialization方法。(AOP 代理通常在这一步生成并返回代理对象)。
d. Bean 创建完成,放入单例池(一个Map)中,供后续使用。
- 调用各种
- 比喻:后厨 (
BeanFactory) 开始按照菜谱 (BeanDefinition) 正式做菜。每一道菜 (Bean) 的制作过程都严格遵守:
a. 准备原材料 (实例化)。
b. 加入配料 (依赖注入)。
c. 烹饪加工 (初始化):- 质检员 (
BeanPostProcessor) 在烹饪前检查一下。 - 大厨按特定步骤 (
@PostConstruct) 烹饪。 - 出锅后,质检员/装饰师 (
BeanPostProcessor) 再进行最后的加工和摆盘(比如创建 AOP 代理)。
d. 做好的菜放入出餐口 (单例池)。
- 质检员 (
阶段五:收尾工作 (餐厅正式开业)
finishRefresh():- 作用:完成所有收尾工作。
- 初始化
LifecycleProcessor,并调用所有实现了Lifecycle接口的 Bean 的start()方法。 - 发布
ContextRefreshedEvent事件,通知所有监听者容器已经初始化完成。 - 比喻:总经理宣布餐厅正式开业!打开大门,播放背景音乐 (
Lifecycle.start()),并向所有会员 (Listener) 发送“本店已开业”的通知 (ContextRefreshedEvent)。
resetCommonCaches(): 重置一些公共的缓存。
至此,ApplicationContext 初始化完毕,可以对外提供服务了。
总结流程图
[开始]
|
v
+-----------------------------+
| 1. 构造 ApplicationContext |
| (调用 refresh() 方法) |
+-----------------------------+
|
v
+-----------------------------+
| 2. 准备阶段 |
| - 创建 BeanFactory |
| - 加载/解析 BeanDefinition|
+-----------------------------+
|
v
+-----------------------------+
| 3. 调用 BeanFactoryPostProcessor |
| (修改 BeanDefinition) |
+-----------------------------+
|
v
+-----------------------------+
| 4. 注册 BeanPostProcessor |
| (准备好对 Bean 实例的加工)|
+-----------------------------+
|
v
+-----------------------------+
| 5. 实例化所有非懒加载单例 Bean|
| - 实例化 |
| - 属性注入 |
| - 初始化 (调用 Aware, |
| BeanPostProcessor, |
| @PostConstruct) |
+-----------------------------+
|
v
+-----------------------------+
| 6. 完成阶段 |
| - 发布 ContextRefreshedEvent|
| - 容器准备就绪 |
+-----------------------------+
|
v
[结束]
理解这个流程的关键在于区分 BeanDefinition (蓝图/菜谱) 和 Bean 实例 (成品),以及 BeanFactoryPostProcessor (修改蓝图) 和 BeanPostProcessor (加工成品) 的作用时机和对象。