Spring Bean 生命周期有哪些阶段?都有哪些扩展点?
系统讲解 Spring 容器启动、Bean 定义处理、实例化、属性注入、初始化、AOP 代理、就绪与销毁的完整生命周期,并梳理 BeanFactoryPostProcessor、BeanPostProcessor、Aware、初始化回调、SmartInitializingSingleton、事件和 SmartLifecycle 等扩展点。
Spring Bean 生命周期有哪些阶段?都有哪些扩展点?
面试回答
Spring Bean 的完整生命周期可以分为:Bean 定义注册与修改、实例化、属性填充、Aware 回调、初始化前处理、初始化、初始化后处理、Bean 就绪,以及容器关闭时的销毁。
容器启动后,首先读取配置并注册 BeanDefinition。BeanDefinitionRegistryPostProcessor 可以继续注册或删除定义,BeanFactoryPostProcessor 可以在 Bean 创建前修改定义。随后 Spring 注册所有 BeanPostProcessor,再创建非懒加载单例。
创建单个 Bean 时,Spring先选择构造方法并实例化对象,再进行依赖注入;随后执行 Aware 回调、BeanPostProcessor#postProcessBeforeInitialization()、@PostConstruct、InitializingBean#afterPropertiesSet() 和自定义 init-method,最后执行 postProcessAfterInitialization()。Spring AOP 通常在初始化后处理阶段返回代理对象。
所有普通非懒加载单例创建完成后,可以使用 SmartInitializingSingleton 或监听 ContextRefreshedEvent 执行全局初始化;需要管理组件启动和停止顺序时,可以使用 SmartLifecycle。容器关闭时,先由 DestructionAwareBeanPostProcessor 执行销毁前处理,其中 CommonAnnotationBeanPostProcessor 会调用 @PreDestroy;之后再执行 DisposableBean#destroy() 和自定义销毁方法。
一句话总结:
Spring 先处理 BeanDefinition,再通过 BeanPostProcessor 贯穿 Bean 的创建过程;Bean 初始化完成不等于容器全部就绪,批量就绪、运行启停和销毁分别有对应扩展点。
一张图看懂
详细讲解
一、先区分两个生命周期
讨论“Spring 生命周期”时,容易把两个概念混在一起:
1. ApplicationContext 容器生命周期
容器负责:
加载配置
→ 注册和修改 BeanDefinition
→ 注册容器级后处理器
→ 创建非懒加载单例
→ 发布刷新完成事件
→ 启动 Lifecycle 组件
→ 关闭并销毁单例
2. 单个 Bean 的生命周期
单个 Bean 经历:
实例化
→ 属性填充
→ Aware 回调
→ 初始化前处理
→ 初始化回调
→ 初始化后处理
→ 对外提供
→ 销毁
BeanFactoryPostProcessor 作用于整个容器中的 Bean 定义;BeanPostProcessor 作用于容器创建的 Bean 实例。两者所处层次不同。
二、阶段一:加载并注册 BeanDefinition
BeanDefinition 是 Spring 创建 Bean 的配置模型,记录了:
- Bean 类型;
- 作用域;
- 是否懒加载;
- 构造参数和属性值;
- 初始化方法和销毁方法;
- 依赖关系;
- 是否允许作为自动注入候选项。
Bean 定义可能来自:
@Component组件扫描;@Bean方法;- XML;
- 程序化注册;
@Import导入。
这一阶段还没有创建普通业务 Bean,处理的是“如何创建 Bean”的元数据。
1. ImportSelector
ImportSelector 根据导入类的元数据,返回需要注册的配置类名称。
适合:
- 按条件组合一组配置;
- 模块化装配;
- 框架自动配置入口。
DeferredImportSelector 会在普通配置类处理之后再决定导入内容,Spring Boot 自动配置大量使用了这类机制。
2. ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar 可以直接向注册表写入 BeanDefinition。
适合:
- 根据注解属性动态注册 Bean;
- 为接口生成代理 BeanDefinition;
- 注册数量或名称在编译期不固定的组件。
3. BeanDefinitionRegistryPostProcessor
它是 BeanFactoryPostProcessor 的子接口,核心能力是操作 BeanDefinitionRegistry:
public interface BeanDefinitionRegistryPostProcessor
extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(
BeanDefinitionRegistry registry);
}
它可以在普通 BeanFactoryPostProcessor 执行前:
- 注册新的 BeanDefinition;
- 删除或替换已有 BeanDefinition;
- 扫描额外的组件;
- 根据外部配置动态扩展容器。
ConfigurationClassPostProcessor 就是重要实现,它负责解析 @Configuration、@ComponentScan、@Import 和 @Bean 等配置。
三、阶段二:修改 BeanDefinition
BeanFactoryPostProcessor
所有 BeanDefinition 基本注册完成后,Spring 调用 BeanFactoryPostProcessor:
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory);
}
它处理的是 BeanDefinition,而不是已经创建好的业务对象。
典型能力包括:
- 修改属性值;
- 修改作用域和懒加载配置;
- 调整自动注入候选状态;
- 解析配置占位符;
- 注册容器级基础设施。
例如 PropertySourcesPlaceholderConfigurer 会把:
order.timeout=${ORDER_TIMEOUT}
中的占位符替换为环境中的实际值。
不要在这个阶段随意调用 getBean()。这会导致普通 Bean 提前创建,使它可能绕过尚未注册完成的 BeanPostProcessor,进而错过依赖注入、AOP 等处理。
四、阶段三:注册 BeanPostProcessor
Spring 会提前创建并注册所有 BeanPostProcessor,使它们能够处理后续创建的 Bean。
基础接口如下:
public interface BeanPostProcessor {
default Object postProcessBeforeInitialization(
Object bean, String beanName) {
return bean;
}
default Object postProcessAfterInitialization(
Object bean, String beanName) {
return bean;
}
}
常见功能很多都建立在 BeanPostProcessor 体系上:
@Autowired、@Value注入;@PostConstruct、@PreDestroy;- AOP 自动代理;
@Async;@Scheduled;- 事务代理;
- 异常转换。
执行顺序
多个后处理器通常按照以下优先级排序:
PriorityOrdered
→ Ordered
→ 未实现排序接口
程序化调用 addBeanPostProcessor() 注册时,主要按照实际注册顺序执行,不应只依赖 Ordered。
五、阶段四:实例化 Bean
Spring 在 AbstractAutowireCapableBeanFactory#createBean() 中进入单个 Bean 的创建流程。
1. 实例化前扩展
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation() 在目标对象真正实例化前执行。
它可以直接返回一个对象,例如代理:
返回 null
→ 继续正常实例化流程
返回非 null
→ 跳过普通实例化、属性填充和初始化流程
→ 对返回对象执行初始化后处理
这属于特殊扩展点,主要用于框架基础设施,不适合承载普通业务初始化。
2. 选择构造方法
Spring需要决定使用:
- 默认构造方法;
- 显式指定的构造方法;
@Autowired构造方法;- 工厂方法;
Supplier。
SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors() 可以参与候选构造方法选择。
3. 创建原始对象
构造方法或工厂方法执行完成后,得到的是原始 Bean 实例。此时依赖属性通常还没有填充,初始化回调也没有执行。
六、阶段五:处理合并后的 BeanDefinition
父子 BeanDefinition、配置覆盖和类型解析完成后,Spring 得到合并后的 BeanDefinition。
MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition() 可以缓存与具体 Bean 类型有关的元数据。
常见用途包括:
- 扫描注入点;
- 缓存注解元数据;
- 为后续属性注入和销毁处理做准备。
AutowiredAnnotationBeanPostProcessor 会在这里分析 @Autowired、@Value 等注入信息,避免每次都重新反射扫描。
七、阶段六:提前暴露引用
对于允许循环依赖处理的单例,Spring可能在属性填充前暴露一个对象工厂:
singletonFactories
→ earlySingletonObjects
→ singletonObjects
这里暴露的不是“已经完成初始化的 Bean”,而是用于打破依赖环的早期引用。
SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference() 可以决定早期暴露什么对象。AOP 自动代理创建器会利用这个扩展点,尽量让循环依赖中的其他 Bean 获得代理引用,而不是直接获得原始对象。
需要注意:
- 这主要处理单例的属性或 Setter 注入循环依赖;
- 构造器循环依赖无法靠提前暴露一个尚未构造完成的对象解决;
- 原型 Bean 的循环依赖也不能通过三级缓存解决;
- 不应把三级缓存当成设计循环依赖的理由。
八、阶段七:属性填充与依赖注入
实例创建后,Spring进入 populateBean(),为对象填充依赖。
1. postProcessAfterInstantiation
InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation() 在对象已经实例化、属性填充尚未开始时执行。
返回 false 可以阻止 Spring继续执行常规属性填充,因此属于影响较大的底层扩展点。
2. postProcessProperties
InstantiationAwareBeanPostProcessor#postProcessProperties() 可以处理属性注入。
典型实现:
AutowiredAnnotationBeanPostProcessor:处理@Autowired和@Value;CommonAnnotationBeanPostProcessor:处理@Resource等 Jakarta 注解。
随后 Spring还会应用 BeanDefinition 中显式配置的属性值。
九、阶段八:Aware 回调
依赖注入完成后,Spring让 Bean 感知容器提供的基础设施。
常见接口包括:
| Aware 接口 | 能够获得的对象 |
|---|---|
BeanNameAware | 当前 Bean 名称 |
BeanClassLoaderAware | Bean 类加载器 |
BeanFactoryAware | 当前 BeanFactory |
EnvironmentAware | Environment |
ApplicationEventPublisherAware | 事件发布器 |
ResourceLoaderAware | 资源加载器 |
ApplicationContextAware | ApplicationContext |
前三个由 BeanFactory 初始化流程直接调用;ApplicationContextAware 等接口通常由 ApplicationContextAwareProcessor 这个 BeanPostProcessor 处理。
Aware 适合获取容器基础设施,但会让业务类与 Spring 接口耦合。普通业务依赖仍应优先采用构造器注入。
十、阶段九:初始化前处理
Spring依次调用所有:
BeanPostProcessor#postProcessBeforeInitialization()
常见用途包括:
- 识别并执行生命周期注解;
- 对 Bean 做初始化前检查;
- 注入容器上下文相关能力;
- 包装或替换 Bean。
@PostConstruct 在哪里执行
@PostConstruct 不是 Spring硬编码在初始化流程中的独立步骤,而是由 CommonAnnotationBeanPostProcessor 在初始化前处理阶段识别并调用。
因此从宏观顺序看:
Aware 回调
→ postProcessBeforeInitialization
→ @PostConstruct
→ InitializingBean
→ 自定义 init-method
十一、阶段十:执行初始化回调
同一个 Bean 同时使用多种初始化机制且方法名不同时,调用顺序是:
@PostConstruct;InitializingBean#afterPropertiesSet();@Bean(initMethod = "...")或 XMLinit-method。
示例:
public class PaymentClient implements InitializingBean {
@PostConstruct
public void validateConfiguration() {
// 校验配置
}
@Override
public void afterPropertiesSet() {
// Spring 接口回调
}
public void init() {
// 自定义初始化方法
}
}
配置:
@Bean(initMethod = "init", destroyMethod = "close")
public PaymentClient paymentClient() {
return new PaymentClient();
}
业务代码通常优先选择 @PostConstruct 或自定义初始化方法,避免为了生命周期回调而强耦合 Spring 接口。
初始化方法适合:
- 校验必要配置;
- 构造只依赖当前 Bean 状态的内存结构;
- 完成本地资源准备。
不适合在初始化方法中启动复杂异步任务,或者访问大量其他 Bean。初始化发生在单例创建过程中,过重或存在交叉访问的逻辑可能延长启动时间,甚至引发初始化死锁。
十二、阶段十一:初始化后处理与 AOP 代理
初始化回调完成后,Spring调用:
BeanPostProcessor#postProcessAfterInitialization()
后处理器可以返回:
- 原对象;
- 包装对象;
- 代理对象;
- 其他兼容对象。
Spring AOP 的自动代理创建器属于 BeanPostProcessor。它会判断当前 Bean 是否匹配 Advisor;如果匹配,就创建 JDK 动态代理或 CGLIB 代理。
因此容器最终对外暴露的对象可能不是原始对象:
原始 Bean
→ 初始化回调
→ AOP 后处理器
→ 代理 Bean
→ singletonObjects
这也解释了为什么在 @PostConstruct 中调用自身的事务方法,通常不会经过当前 Bean 的事务代理:初始化回调执行时,完整代理尚未作为最终 Bean 对外提供,而且调用方式仍然是对象内部的 this 调用。
十三、Bean 初始化完成后,容器何时才算就绪
单个 Bean 完成初始化,并不代表所有单例都已经创建完成。
1. SmartInitializingSingleton
所有常规非懒加载单例完成实例化后,Spring调用:
public interface SmartInitializingSingleton {
void afterSingletonsInstantiated();
}
适合:
- 需要确认其他单例已经准备完成的检查;
- 汇总多个 Bean 建立注册表;
- 避免因为主动
getBeansOfType()导致意外提前初始化。
它只对单例 Bean 有意义。
2. ContextRefreshedEvent
容器刷新完成后会发布 ContextRefreshedEvent。
可以使用:
@EventListener(ContextRefreshedEvent.class)
public void onContextReady() {
// 容器刷新完成后的逻辑
}
适合需要基于 ApplicationContext 刷新完成事件触发的逻辑。
需要注意,ApplicationContext 可以被刷新多次时,监听逻辑也可能执行多次,业务代码应考虑幂等。
3. SmartLifecycle
需要启动和停止后台消费者、调度器、长连接等有运行状态的组件时,优先考虑 SmartLifecycle:
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
它支持:
- 容器刷新后自动启动;
phase控制启动和停止顺序;- 异步停止完成回调;
- 运行状态检查。
启动时通常先启动较小的 phase,关闭时反向停止。依赖其他组件的服务应根据依赖关系设计 phase,而不是随意填写。
十四、阶段十二:容器关闭与 Bean 销毁
容器正常关闭时,Spring先停止需要运行期管理的 Lifecycle 组件,再销毁受管理的单例 Bean。
同一个 Bean 配置多种销毁机制且方法名不同时,可以按下面的层次理解:
DestructionAwareBeanPostProcessor#postProcessBeforeDestruction()执行销毁前处理,其中CommonAnnotationBeanPostProcessor调用@PreDestroy;DisposableBean#destroy();@Bean(destroyMethod = "...")或 XMLdestroy-method。
因此,@PreDestroy 不是脱离后处理器单独运行的一轮流程,而是 DestructionAwareBeanPostProcessor 体系提供的具体生命周期回调。
销毁阶段适合:
- 停止后台任务;
- 关闭线程池;
- 关闭网络连接;
- 刷新缓冲数据;
- 注销服务或监听器。
原型 Bean 的特殊点
对于 prototype Bean,Spring负责创建和注入,但把实例交给调用方后通常不再跟踪其完整生命周期,因此不会自动调用普通销毁回调。
如果原型 Bean 持有必须释放的资源,应由使用方显式关闭,或者改用更合适的作用域和资源管理方式。
应用被强制终止时,销毁回调也不保证一定执行。重要数据不能只依赖 @PreDestroy 才落盘。
十五、常见扩展点应该怎么选择
| 目标 | 优先考虑的扩展点 |
|---|---|
| 导入一组配置类 | ImportSelector、DeferredImportSelector |
| 动态注册 BeanDefinition | ImportBeanDefinitionRegistrar、BeanDefinitionRegistryPostProcessor |
| 在创建 Bean 前修改配置元数据 | BeanFactoryPostProcessor |
| 自定义注入、实例化或代理 | BeanPostProcessor 及其子接口 |
| 获得 Bean 名称或容器对象 | 对应的 Aware 接口 |
| 依赖注入完成后校验当前 Bean | @PostConstruct 或自定义 initMethod |
| 所有普通单例创建完成后执行 | SmartInitializingSingleton |
| 容器刷新完成后执行 | 监听 ContextRefreshedEvent |
| 管理后台组件的启动和停止 | SmartLifecycle |
| 释放 Bean 自身资源 | @PreDestroy 或自定义 destroyMethod |
| 创建复杂对象并把产物交给容器 | FactoryBean |
| 定义新的 Bean 作用域 | 实现 Scope 并注册 |
十六、FactoryBean 是什么扩展点
普通 BeanDefinition 描述“这个类本身如何被创建”;FactoryBean<T> 描述“通过这个工厂生产什么对象”。
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
假设 Bean 名称为 clientFactory:
getBean("clientFactory")
→ 获得 FactoryBean 生产的对象
getBean("&clientFactory")
→ 获得 FactoryBean 本身
它适合创建代理、客户端、会话工厂等构造过程复杂的对象。MyBatis 与 Spring 集成中的 SqlSessionFactoryBean 就使用了这一机制。
十七、自定义 Scope
默认常见作用域包括:
singleton;prototype;- Web 环境中的
request、session、application、websocket。
实现 org.springframework.beans.factory.config.Scope 并通过 ConfigurableBeanFactory#registerScope() 注册,可以定义租户、任务、线程上下文等自定义作用域。
自定义 Scope 必须明确:
- 实例保存在哪里;
- 何时创建和复用;
- 上下文结束时如何执行销毁回调;
- 上下文如何跨线程传播;
- 作用域 Bean 被单例依赖时是否需要代理。
十八、常见误区
1. Bean 构造完成就可以使用
构造完成后还没有完成属性注入、Aware、初始化回调和后处理,不能把构造完成等同于 Bean 就绪。
2. @PostConstruct 发生在 AOP 代理之后
@PostConstruct 属于初始化前处理阶段;AOP 代理通常在初始化后处理中形成并作为最终对象暴露。
3. BeanPostProcessor 只在初始化前后各做一次简单回调
基础接口只有两个方法,但其子接口参与实例化前、构造器选择、属性填充、早期代理、合并定义和销毁等多个阶段。
4. BeanFactoryPostProcessor 可以直接修改业务 Bean
它应该修改 BeanDefinition。调用 getBean() 可能造成提前实例化并绕过其他基础设施。
5. Bean 生命周期结束一定会执行销毁方法
原型 Bean、进程强杀、机器掉电等场景都可能无法执行正常销毁回调。
6. 所有初始化逻辑都放进 @PostConstruct
Bean 自身的轻量校验可以放在 @PostConstruct;依赖所有单例完成、容器刷新完成或需要启停管理的任务,应分别选择 SmartInitializingSingleton、容器事件或 SmartLifecycle。
十九、源码主线
阅读 Spring Bean 生命周期源码时,可以沿着以下调用链:
AbstractApplicationContext#refresh
├── invokeBeanFactoryPostProcessors
├── registerBeanPostProcessors
├── finishBeanFactoryInitialization
│ └── preInstantiateSingletons
│ └── getBean
│ └── AbstractAutowireCapableBeanFactory#createBean
│ └── doCreateBean
│ ├── createBeanInstance
│ ├── applyMergedBeanDefinitionPostProcessors
│ ├── addSingletonFactory
│ ├── populateBean
│ └── initializeBean
│ ├── invokeAwareMethods
│ ├── applyBeanPostProcessorsBeforeInitialization
│ ├── invokeInitMethods
│ └── applyBeanPostProcessorsAfterInitialization
└── finishRefresh
销毁主线可以关注:
AbstractApplicationContext#close
→ doClose
→ LifecycleProcessor#onClose
→ destroyBeans
→ DisposableBeanAdapter#destroy
二十、复习检查清单
- 能否区分容器生命周期和单个 Bean 生命周期;
- 能否说明
BeanFactoryPostProcessor与BeanPostProcessor的区别; - 是否知道依赖注入发生在实例化之后、初始化之前;
- 能否说清
@PostConstruct、InitializingBean、自定义 init 方法的顺序; - 是否知道 AOP 代理通常在哪个阶段创建;
- 能否解释三级缓存和
getEarlyBeanReference()的作用; - 是否知道单个 Bean 初始化完成后还有哪些容器级扩展点;
- 能否说明
SmartInitializingSingleton、容器事件和SmartLifecycle的使用边界; - 是否知道销毁回调的顺序以及原型 Bean 的限制;
- 能否按目标选择定义级、实例级、运行级和销毁级扩展点。
评论与讨论
回复 :
留下你的想法