Spring 启动流程详解
在面试中,经常会被问到 Spring 的启动流程,这不仅考察你对 Spring 框架的理解深度,也反映了你对框架底层机制的掌握程度。本文将详细介绍 Spring 容器的启动流程(以 AnnotationConfigApplicationContext 为例),并分析每个步骤的核心作用。
Spring 启动流程概述
Spring 的启动流程主要围绕 ApplicationContext 的创建和初始化展开。无论是基于 XML 配置还是注解配置,其核心逻辑都集中在 AbstractApplicationContext 的 refresh() 方法中。以下是启动流程的详细步骤:
1. 容器初始化:创建 ApplicationContext
启动 Spring 应用的第一步是实例化一个 ApplicationContext。以注解配置为例,我们通常会使用 AnnotationConfigApplicationContext:
java
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
- 作用 :这一步会加载配置类(例如
AppConfig),并初始化 Spring 容器的基本结构。 - 细节 :构造方法会调用
this()初始化BeanFactory(默认是DefaultListableBeanFactory),并注册配置类本身作为一个 Bean。
2. 准备刷新:prepareRefresh()
调用 refresh() 方法是 Spring 启动的核心入口,在此之前会执行一些准备工作:
- 初始化属性源:加载环境变量、系统属性等。
- 验证必要属性:检查是否有必须的配置项缺失。
- 准备监听器:初始化事件监听器,为后续事件发布做准备。
3. 创建并配置 BeanFactory:obtainFreshBeanFactory()
这一步会创建一个全新的 BeanFactory,并加载 Bean 定义:
- 加载配置 :如果是注解配置,会扫描
@ComponentScan指定的包路径,解析@Component、@Service等注解,生成BeanDefinition。 - 注册 Bean 定义 :将扫描到的 Bean 定义注册到
BeanFactory中。 - 分析 :
BeanFactory是 Spring 的核心组件,负责管理 Bean 的生命周期。此时尚未创建 Bean 实例,只是注册了元信息。
4. 准备 BeanFactory:prepareBeanFactory()
对 BeanFactory 进行进一步配置,使其具备完整功能:
- 添加 BeanPostProcessor :例如
ApplicationContextAwareProcessor,用于支持Aware接口。 - 设置类加载器:配置默认的类加载器,通常是当前线程的上下文类加载器。
- 忽略某些接口 :如
Aware接口,避免被自动注入。
5. 执行 BeanFactory 后处理器:invokeBeanFactoryPostProcessors()
这一步会调用所有 BeanFactoryPostProcessor,对 BeanDefinition 进行修改:
- 典型例子 :
ConfigurationClassPostProcessor会解析@Configuration类,处理@Bean方法,生成额外的BeanDefinition。 - 分析:这是 Spring 支持注解配置的关键步骤,允许动态调整 Bean 定义。
6. 注册 BeanPostProcessor:registerBeanPostProcessors()
将所有 BeanPostProcessor 注册到容器中,准备在 Bean 创建时介入:
- 作用:支持 AOP、依赖注入等功能的实现。
- 顺序 :根据
PriorityOrdered和Ordered接口排序执行。
7. 初始化消息源和事件多播器
- 消息源 :初始化
MessageSource,支持国际化。 - 事件多播器 :初始化
ApplicationEventMulticaster,用于事件发布和监听。
8. 初始化其他特殊 Bean:onRefresh()
子类可以重写此方法,执行特定初始化逻辑。例如,Spring Boot 在此初始化嵌入式 Web 容器。
9. 注册事件监听器:registerListeners()
将所有 ApplicationListener 注册到事件多播器中,确保事件机制正常工作。
10. 实例化所有单例 Bean:finishBeanFactoryInitialization()
这一步是 Bean 创建的高潮:
- 预实例化单例 :调用
beanFactory.preInstantiateSingletons(),触发所有非懒加载单例 Bean 的创建。 - 生命周期 :
- 实例化:通过构造方法创建对象。
- 属性填充:执行依赖注入。
- 初始化 :调用
InitializingBean.afterPropertiesSet()或@PostConstruct方法。 - AOP 代理:如果需要,生成代理对象。
- 分析:这是 Spring 依赖注入和 AOP 的核心实现阶段。
11. 完成刷新:finishRefresh()
- 发布事件 :发布
ContextRefreshedEvent,通知容器已就绪。 - 初始化 Lifecycle Bean :调用实现了
Lifecycle接口的 Bean 的start()方法。
12. 关闭钩子注册(可选)
如果是通过 ConfigurableApplicationContext 创建,会注册 JVM 关闭钩子,确保容器优雅关闭。
流程图示
以下是简化的流程图,便于理解:
scss
初始化 ApplicationContext
↓
prepareRefresh()
↓
obtainFreshBeanFactory()
↓
prepareBeanFactory()
↓
invokeBeanFactoryPostProcessors()
↓
registerBeanPostProcessors()
↓
finishBeanFactoryInitialization()
↓
finishRefresh()
关键点分析
- BeanFactory vs ApplicationContext :
BeanFactory是基础容器,负责 Bean 的创建和管理。ApplicationContext是高级容器,增加了事件机制、国际化等功能。
- 依赖注入的实现 :
- 在
finishBeanFactoryInitialization()中通过getBean()递归解决依赖。
- 在
- AOP 的实现 :
- 通过
BeanPostProcessor(如AbstractAutoProxyCreator)在 Bean 初始化后生成代理。
- 通过
- 扩展点 :
BeanFactoryPostProcessor和BeanPostProcessor是开发者扩展 Spring 的主要途径。
常见面试问题
- Q1:refresh() 方法的作用是什么?
- 答:
refresh()是 Spring 容器启动的核心方法,负责初始化容器、加载 Bean 定义、创建 Bean 实例等全流程。
- 答:
- Q2:Bean 是如何被创建的?
- 答:在
finishBeanFactoryInitialization()中,通过getBean()方法触发实例化、依赖注入和初始化。
- 答:在
- Q3:AOP 在哪个阶段生效?
- 答:在 Bean 初始化后,由
BeanPostProcessor实现代理对象的生成。
- 答:在 Bean 初始化后,由
总结
Spring 的启动流程是一个精心设计的过程,从配置加载到 Bean 实例化,体现了其高度模块化和可扩展性。理解这一流程不仅能应对面试,还能帮助开发者更好地调试和优化 Spring 应用。希望这篇博客能为你提供清晰的思路!