基于本文回答
0
评论

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() 方法的执行顺序,拆解为几个主要阶段和关键步骤。

阶段一:准备阶段 (餐厅开业前的准备工作)

  1. prepareRefresh():

    • 作用:设置容器的启动时间,激活状态标志,初始化属性源(property sources)。
    • 比喻:总经理记录下餐厅的开业时间,挂上“正在准备”的牌子,并检查一下今天的原材料供应情况。
  2. obtainFreshBeanFactory():

    • 作用:创建或获取一个全新的 BeanFactory(通常是 DefaultListableBeanFactory),并加载所有的 BeanDefinition
    • 对于 XML 配置:此时会创建 XmlBeanDefinitionReader 来解析 XML 文件,将 <bean> 标签转换成一个个 BeanDefinition 对象,然后注册到 BeanFactory 中。
    • 对于注解配置:此时会创建 AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner 来扫描指定的包,找到带有 @Component, @Service, @Configuration 等注解的类,将它们也转换成 BeanDefinition 注册到 BeanFactory 中。
    • 比喻:总经理建立了一个全新的后厨 (BeanFactory),并把菜单 (<bean>@Component) 上的所有菜谱 (BeanDefinition) 都整理好,放进了后厨的菜谱架上。
    • 关键点此时,只有菜谱 (BeanDefinition),还没有任何一道真正的菜 (Bean 实例) 被做出来。
  3. prepareBeanFactory(beanFactory):

    • 作用:对 BeanFactory 进行一系列的配置。比如设置类加载器(ClassLoader)、添加一些默认的 BeanPostProcessor(例如 ApplicationContextAwareProcessor,它能让 Bean 获取到 ApplicationContext 实例)。
    • 比喻:总经理给后厨配置好基本的厨具(类加载器),并告诉厨师们一些基本规则(比如如何处理特殊需求的订单)。
  4. postProcessBeanFactory(beanFactory):

    • 作用:一个模板方法,留给子类扩展。例如,WebApplicationContext 会在这里添加对 ServletContext 的支持。
    • 比喻:如果这家餐厅是连锁店(子类),那么在这里会进行一些该分店特有的准备工作。

阶段二:应用后置处理器 (顾问和质检员入场)

  1. invokeBeanFactoryPostProcessors(beanFactory):

    • 作用这是一个非常重要的步骤! Spring 会在这里找到所有实现了 BeanFactoryPostProcessor 接口的 Bean,并依次执行它们。
    • 这些处理器可以读取、修改所有 BeanDefinition。最典型的例子是 PropertySourcesPlaceholderConfigurer,它会扫描 BeanDefinition 中的 ${...} 占位符,并用配置文件(如 application.properties)中的值替换它们。
    • 比喻:厨房顾问 (BeanFactoryPostProcessor) 入场,他会检查所有的菜谱 (BeanDefinition)。发现菜谱里写着“盐(适量)”,顾问会根据老板的要求,把它们都改成“盐(5克)”。
    • 关键点:它操作的是 Bean 的定义 (菜谱),而不是 Bean 的实例 (成品菜)
  2. registerBeanPostProcessors(beanFactory):

    • 作用:找到所有实现了 BeanPostProcessor 接口的 Bean,并将它们注册到 BeanFactory 的拦截器链中。
    • 注意:这里只是“注册”,并不会立即执行。它们会在后续 Bean 实例化的过程中被回调。
    • 常见的 BeanPostProcessor 有:AutowiredAnnotationBeanPostProcessor (处理 @Autowired 注解)、CommonAnnotationBeanPostProcessor (处理 @PostConstruct, @PreDestroy 注解)、以及 AOP 相关的处理器,它们负责创建代理对象。
    • 比喻:总经理把所有的质检员/装饰师 (BeanPostProcessor) 都请到后厨,让他们站好位置,准备对后面出锅的每一道菜进行检查和装饰。

阶段三:初始化内部组件

  1. initMessageSource(): 初始化国际化(i18n)消息源。
  2. initApplicationEventMulticaster(): 初始化事件广播器,用于支持 Spring 的事件驱动模型 (ApplicationEvent)。
  3. onRefresh(): 另一个模板方法,留给子类初始化其他特殊的 Bean。例如,Spring MVC 的 DispatcherServlet 会在这里初始化它的 Web 组件。

阶段四:实例化 Bean (真正开始做菜)

  1. registerListeners(): 将配置文件中定义的 ApplicationListener 注册到事件广播器中。

  2. finishBeanFactoryInitialization(beanFactory):

    • 作用核心中的核心! 实例化所有剩余的、非懒加载的单例(Singleton)Bean。
    • 这个过程会遍历 BeanFactory 中所有的 BeanDefinition
    • 对于每一个需要创建的 Bean,会触发 getBean() -> doGetBean() 的调用链。
    • Bean 的生命周期在这里完整体现:
      a. 实例化 (Instantiation):通过反射创建 Bean 的实例对象。
      b. 属性填充 (Populate):处理 @Autowired 等注解,进行依赖注入。
      c. 初始化 (Initialization)
      • 调用各种 Aware 接口(如 BeanNameAware, ApplicationContextAware)。
      • 执行 BeanPostProcessorpostProcessBeforeInitialization 方法。
      • 调用 @PostConstruct 注解的方法或 InitializingBean 接口的 afterPropertiesSet 方法。
      • 执行 BeanPostProcessorpostProcessAfterInitialization 方法。(AOP 代理通常在这一步生成并返回代理对象)。
        d. Bean 创建完成,放入单例池(一个 Map)中,供后续使用。
    • 比喻:后厨 (BeanFactory) 开始按照菜谱 (BeanDefinition) 正式做菜。每一道菜 (Bean) 的制作过程都严格遵守:
      a. 准备原材料 (实例化)。
      b. 加入配料 (依赖注入)。
      c. 烹饪加工 (初始化):
      • 质检员 (BeanPostProcessor) 在烹饪前检查一下。
      • 大厨按特定步骤 (@PostConstruct) 烹饪。
      • 出锅后,质检员/装饰师 (BeanPostProcessor) 再进行最后的加工和摆盘(比如创建 AOP 代理)。
        d. 做好的菜放入出餐口 (单例池)。

阶段五:收尾工作 (餐厅正式开业)

  1. finishRefresh():

    • 作用:完成所有收尾工作。
    • 初始化 LifecycleProcessor,并调用所有实现了 Lifecycle 接口的 Bean 的 start() 方法。
    • 发布 ContextRefreshedEvent 事件,通知所有监听者容器已经初始化完成。
    • 比喻:总经理宣布餐厅正式开业!打开大门,播放背景音乐 (Lifecycle.start()),并向所有会员 (Listener) 发送“本店已开业”的通知 (ContextRefreshedEvent)。
  2. resetCommonCaches(): 重置一些公共的缓存。

至此,ApplicationContext 初始化完毕,可以对外提供服务了。

总结流程图

plaintext
[开始]
  |
  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 (加工成品) 的作用时机和对象。

右滑查看面试常问