Bean 的创建与生命周期
一个 Spring Bean 从定义变成可用对象,要经过实例化、依赖填充、初始化前后处理和发布。容器关闭时再按作用域执行停止与销毁协议。
1. 创建流程从实例化与依赖解析开始
简化后的单例创建路径是:
- 选择构造器或工厂方法并创建原始实例。
- 填充属性,解析
@Autowired、@Value等依赖。 - 调用 Aware 回调,让确有需要的组件取得 Bean 名称、工厂或上下文。
- 执行初始化前的 BeanPostProcessor。
- 执行初始化回调。
- 执行初始化后的 BeanPostProcessor,可能返回代理。
- 将最终对象作为可用 Bean 发布。
不同扩展点会插入更细的步骤,这条主线用于判断对象在某个时刻是否已经具备全部依赖、是否已经被代理。
2. 初始化回调有明确顺序
常用初始化方式:
@PostConstruct
void validateConfiguration() {
// 检查本地配置,不执行不可控的长时间任务
}
同一个 Bean 同时配置多种方式时,常见顺序为:
@PostConstructInitializingBean.afterPropertiesSet()- 自定义
init-method
优先使用 @PostConstruct 或自定义初始化方法,避免业务类不必要地依赖 Spring 接口。
3. BeanPostProcessor 可以替换最终对象
初始化前处理器常用于注解生命周期等逻辑;初始化后处理器可以返回包装对象或代理。AOP 自动代理创建器就在这一阶段参与,因此从容器取得的引用可能不是原始实例。
初始化方法在原始 Bean 上执行,不能依赖事务或其他代理拦截器已经生效。需要代理生效后的启动任务时,可使用 SmartInitializingSingleton、上下文刷新事件或更明确的应用启动机制。
4. “初始化完成”不等于服务已经就绪
Bean 构造和 @PostConstruct 中建立外部连接、全量预热或启动长任务,会延长上下文刷新,失败时也难区分阶段。
可以将启动拆成:配置校验、必要资源建立、后台预热和 readiness。只有不能服务时才让健康状态失败,非关键预热应有超时、指标和降级。
5. 销毁回调负责释放容器拥有的资源
容器关闭 singleton 时,常见顺序包括:
@PreDestroyDisposableBean.destroy()- 自定义
destroy-method
线程池、客户端和文件句柄应在明确的所有者处关闭。销毁逻辑要有期限,避免优雅关闭无限等待。
prototype Bean 的初始化回调会执行,但容器交付实例后不再完整跟踪,配置的销毁回调不会自动调用;使用者要负责清理。
6. 生命周期日志要有 Bean 与阶段
排查启动慢或失败时,记录 Bean 名称、阶段、耗时和异常根因。不要在每个 Bean 打大量普通日志;可以利用 Spring Boot 启动步骤、JFR 或专门的启动分析工具形成时间线。
7. 常见问题
7.1 @PostConstruct 中调用自己的事务方法会生效吗
通常不能依赖。此时初始化回调发生在最终代理发布之前,自调用也不会经过外部代理。应把事务工作放在容器刷新后的独立 Bean 调用或显式事务模板中。
7.2 Bean 销毁后一定会执行 @PreDestroy 吗
只有容器按正常关闭流程管理该 Bean 时才有机会执行。进程被强制终止、崩溃,或对象不在完整管理范围内,都不能依赖回调完成关键一致性操作。
8. 面试题
8.1 Spring Bean 从创建到销毁经历哪些关键阶段
出现公司:美团
考察重点
- 实例化、依赖填充和处理器顺序。
- 初始化后可能返回代理。
- 销毁与 prototype 边界。
相关内容:第 1 节“创建流程从实例化与依赖解析开始”至第 5 节“销毁回调负责释放容器拥有的资源”。
参考回答
容器先按 BeanDefinition 实例化对象并填充依赖,然后执行 Aware 回调、初始化前处理器、@PostConstruct 等初始化回调和初始化后处理器。后处理器可能把原始对象包装成代理,容器最终发布的是处理后的引用。
上下文关闭时,容器对其管理的 singleton 执行 @PreDestroy 等销毁回调。prototype 交付后不再由容器完整跟踪,使用方要自行清理;强制退出也不能保证任何回调执行。