Spring Boot 加载与 Bean 加载过程
Spring Boot 加载与 Bean 加载过程
1. 引言
Spring Boot 的核心在于约定大于配置,它通过自动配置让开发者可以快速、简便地搭建项目。在底层,Spring Boot 仍然依赖于 Spring Framework 的 ApplicationContext(常见如 AnnotationConfigApplicationContext 或 AnnotationConfigServletWebServerApplicationContext 等)来完成 Bean 的创建、事件发布以及容器的生命周期管理。
了解 Spring Boot 的加载过程和 Bean 加载过程,有助于我们排查各类启动期间的问题(例如循环依赖、初始化过程、Bean 破坏(destroy)等),并对 Spring Boot 原理有更深入的掌握。

2. Spring Boot 的整体启动流程与事件
Spring Boot 应用通常以 SpringApplication.run() 方法作为入口,其背后触发了多个事件和初始化过程。下面简单梳理下常见的核心事件与过程节点(以下事件顺序基于 Spring Boot 2.x/3.x 的通用流程,具体事件名或顺序在不同版本中可能会略有变化):
-
ApplicationStartingEvent
- 在运行SpringApplication.run()时最先发布。此时SpringApplication对象已创建,但还未开始读取配置或准备环境。 -
ApplicationEnvironmentPreparedEvent
- Spring Boot 开始准备并构建Environment(包含propertySources、profiles 等信息)。 -
ApplicationContextInitializedEvent
- 在ApplicationContext创建并准备初始化(但还没完成)时发布。此时已经确定要使用哪种类型的ApplicationContext(web 环境则是AnnotationConfigServletWebServerApplicationContext,普通 Java 程序则可能是AnnotationConfigApplicationContext)。 -
ApplicationPreparedEvent
-Environment已经准备好,且上下文(ApplicationContext)已经创建,但此时 Bean 还未加载完成。一些BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor可以在此阶段修改 Bean 定义信息。 -
ContextRefreshedEvent
- Spring 调用refresh()方法完成上下文刷新,触发 Bean 的创建与初始化,完成自动配置等。
- Bean 的加载与初始化正是在refresh()的这个阶段中执行,随后所有单例 Bean 将被实例化并注入依赖。 -
ApplicationStartedEvent
- 上下文已刷新,但还没有调用CommandLineRunner和ApplicationRunner等接口方法。 -
ApplicationReadyEvent
- 所有 Bean 已加载完成,并且CommandLineRunner、ApplicationRunner已经执行完毕,应用已经可以对外提供服务。 -
容器运行中
- 应用开始正常对外提供服务,接受请求。 -
ContextClosedEvent(当应用关闭时)
- 触发容器关闭事件,执行 Bean 的销毁方法及相关处理(如执行DisposableBean、@PreDestroy注解标注的方法),此后容器退出或等待关闭钩子(shutdown hook)。
整体而言,Spring Boot 的事件会贯穿整个启动和销毁过程,通过监听这些事件可进行一些自定义操作,比如在应用准备好时去加载特定资源,或者在关闭时做清理工作。
3. Spring 容器中 Bean 的加载与初始化流程
3.1 refresh() 方法中的关键流程
Spring Boot 在准备好环境后,会调用 AbstractApplicationContext#refresh() 来完成 BeanFactory 的创建、BeanDefinition 的加载和注册、BeanPostProcessor 的注册以及 Bean 的初始化等逻辑。其核心流程简要说明如下:
- 准备上下文环境
- 为上下文设置一些容器标识、启动时间戳、监听器等。 - BeanFactory 初始化
- 创建或获取BeanFactory。在注解驱动场景中,通常是AnnotationConfigApplicationContext或AnnotationConfigServletWebServerApplicationContext。 - 执行
BeanFactoryPostProcessor
- 包括ConfigurationClassPostProcessor等,这里会扫描注解、解析配置类并注册更多的 BeanDefinition。 - 注册
BeanPostProcessor
- 用于在 Bean 初始化前后插入自定义逻辑,比如自动注入、代理生成(AOP)等。 - 初始化消息源、事件广播器
- 用于国际化消息处理及事件发布机制。 - 实例化所有剩余的单例 Bean
- 此步骤中会正式调用AbstractAutowireCapableBeanFactory#doCreateBean()等方法进行 Bean 实例化、依赖注入和初始化。 - 完成刷新
- 发布ContextRefreshedEvent,此时容器初始化完成。
3.2 AbstractAutowireCapableBeanFactory#doCreateBean() 源码解析
在 Spring 创建 Bean 的时候,会调用 doCreateBean() 方法来完成实际的 Bean 实例化、属性注入和初始化。下面是你提供的 doCreateBean() 方法简要部分源码并作说明:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args)
throws BeanCreationException {
// 1. 实例化 Bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 2. 调用 MergedBeanDefinitionPostProcessor
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
mbd.markAsPostProcessed();
}
}
// 3. 解决循环依赖(如果允许)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences
&& isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 4. 属性填充与初始化
Object exposedObject = bean;
try {
// 填充属性(依赖注入,包含 @Autowired/@Resource 等)
populateBean(beanName, mbd, instanceWrapper);
// 调用 initializeBean,执行 BeanPostProcessor 的前后置处理,触发 @PostConstruct 等
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// 处理异常
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
// 5. 如果有循环依赖,需要把早期引用替换回来
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// ...
}
}
// 6. 注册销毁方法(DisposableBean 或 @PreDestroy 等)
registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
}
从上面可以看出,doCreateBean() 核心阶段包括:
- 实例化:通过构造函数或工厂方法创建出 Bean 的原始对象。
- 后置处理(MergedBeanDefinitionPostProcessor):对合并后的 BeanDefinition 进行一些附加处理;
- 循环依赖处理:如果允许循环依赖,会提前放入
singletonFactories,让其他 Bean 注入时能获取到“早期”对象引用; - 属性填充:执行真正的依赖注入,比如自动注入
@Autowired、@Resource; - 初始化:包括执行
BeanPostProcessor#postProcessBeforeInitialization/@PostConstruct/InitializingBean#afterPropertiesSet/BeanPostProcessor#postProcessAfterInitialization; - 注册销毁方法:如果实现了
DisposableBean或声明了@PreDestroy,会被记录下来在容器关闭时执行。
4. Bean 何时销毁,容器何时销毁?
4.1 容器关闭触发的时机
- 正常关闭:在 Spring Boot 中,如果是正常退出(例如调用
applicationContext.close()或者用户在 IDE/控制台按下停止),则会触发ContextClosedEvent,随后按顺序销毁所有单例 Bean。 - 异常关闭:如果进程被强制杀死(如
kill -9),则来不及回调 Spring 的钩子方法,Bean 的销毁方法将不会被执行。
Spring Boot 默认会注册一个 JVM 的 shutdown hook,当 JVM 退出时,会回调 close(),从而让容器有机会执行销毁逻辑。
4.2 Bean 的销毁过程
在 registerDisposableBeanIfNecessary() 中,Spring 会检查 Bean 是否实现了以下接口或注解:
DisposableBean:如果实现了该接口,容器关闭时会调用其destroy()方法;@PreDestroy注解:如果定义了该注解的方法,也会被调用;- XML 配置中的 destroy-method:基于传统 XML 配置可以指定自定义销毁方法名称;
最后容器关闭时,会遍历所有已注册的销毁信息(DisposableBeanAdapter),并逐一调用这些销毁方法。
5. CommandLineRunner 和 ApplicationRunner 的执行时机
Spring Boot 在完成容器刷新(ContextRefreshedEvent 发布)后,会发布 ApplicationStartedEvent 和 ApplicationReadyEvent。而执行 CommandLineRunner 和 ApplicationRunner 的逻辑一般会在 ApplicationStartedEvent 之后、ApplicationReadyEvent 之前完成:
CommandLineRunner:传入String... args参数;ApplicationRunner:传入ApplicationArguments参数。
也就是说,这些 Runner 接口的执行点是在容器的所有单例 Bean 已经初始化完毕 之后。若需要在容器刚刚就绪时执行一些初始化任务或者数据加载操作,则可以把逻辑放到 Runner 中。
6. 小结
Spring Boot 的加载过程可以概括为:
1. 创建并启动 SpringApplication;
2. 发布初始事件(ApplicationStartingEvent 等);
3. 准备并创建 ApplicationContext 环境(ApplicationEnvironmentPreparedEvent、ApplicationContextInitializedEvent);
4. 加载配置、注册 BeanDefinition,调用各种 Processor 以及后置处理器(ApplicationPreparedEvent、ContextRefreshedEvent);
5. 初始化所有单例 Bean(doCreateBean() 等过程:实例化 - 属性注入 - 初始化 - 注册销毁方法);
6. 执行 Runner(CommandLineRunner、ApplicationRunner);
7. 发布 ApplicationReadyEvent,应用对外提供服务;
8. 运行中,等待关闭钩子或主动调用 context.close();
9. 触发容器关闭,发布 ContextClosedEvent,执行 Bean 的销毁逻辑(DisposableBean、@PreDestroy 等);
10. 容器结束运行。