这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Bean 生命周期描述一个对象从 BeanDefinition 到可使用、再到销毁的完整过程。事务、异步、配置绑定、初始化校验、动态代理都发生在生命周期不同阶段。
3.1 总体流程
BeanDefinition 加载
-> 实例化前处理
-> 构造对象
-> 属性填充
-> Aware 回调
-> BeanPostProcessor.before
-> 初始化回调
-> BeanPostProcessor.after
-> Bean 就绪
-> 容器关闭
-> 销毁回调
Spring 实际执行细节较复杂,但这条主线非常稳定。
3.2 实例化
常用方式:
- 反射调用构造器;
- 工厂方法实例化;
Supplier实例化;FactoryBean.getObject()。
构造器选择:
@Service
public class OrderService {
private final OrderRepository repository;
public OrderService(OrderRepository repository) {
this.repository = repository;
}
}
如果只有一个构造器,Spring 通常可直接使用。多个构造器时:
- 可用
@Autowired标注首选; - 可用
@ConstructorProperties配合; - 应尽量保持构造器清晰。
3.3 属性填充
实例化后,Spring 会注入依赖和配置值。
@Component
public class PayClient {
@Value("${pay.timeout:3s}")
private Duration timeout;
}
更推荐构造器绑定:
@Component
public class PayClient {
private final Duration timeout;
public PayClient(@Value("${pay.timeout:3s}") Duration timeout) {
this.timeout = timeout;
}
}
属性填充阶段也可能触发类型转换、SpEL 求值和依赖解析失败。
3.4 Aware 回调
常用 Aware:
| 接口 | 获得内容 |
|---|---|
BeanNameAware |
Bean 名称 |
BeanClassLoaderAware |
类加载器 |
BeanFactoryAware |
BeanFactory |
EnvironmentAware |
Environment |
ApplicationContextAware |
ApplicationContext |
示例:
@Component
public class ContextHolder implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
}
Aware 会把容器对象带进业务代码,增加耦合。非框架组件应优先使用构造器注入;只有通用基础设施才适合使用 Aware。
3.5 初始化回调
三种常见方式:
@Component
public class CacheLoader implements InitializingBean {
@Override
public void afterPropertiesSet() {
reload();
}
}
JSR-250:
@PostConstruct
public void init() {
reload();
}
声明式:
@Bean(initMethod = "start", destroyMethod = "close")
public StorageClient storageClient() {
return new StorageClient();
}
推荐顺序:
- 业务 Bean 优先
@PostConstruct,简单直观; - 第三方对象优先
@Bean(initMethod, destroyMethod),不侵入库代码; - 框架内部才实现
InitializingBean。
3.6 BeanPostProcessor
BeanPostProcessor 在初始化前后介入:
@Component
public class AuditBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof Auditable auditable) {
return Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("before " + method.getName());
return method.invoke(bean, args);
});
}
return bean;
}
}
典型内置处理器:
| 处理器 | 作用 |
|---|---|
| AutowiredAnnotationBeanPostProcessor | 解析 @Autowired |
| CommonAnnotationBeanPostProcessor | @PostConstruct、@PreDestroy、资源注入 |
| ApplicationContextAwareProcessor | Aware 回调 |
| AsyncAnnotationBeanPostProcessor | @Async 代理 |
| AbstractAutoProxyCreator | AOP 代理 |
BeanPostProcessor 会影响所有 Bean,应保持轻量、稳定。
3.7 销毁回调
容器关闭时:
发布 ContextClosedEvent
-> 销毁单例
-> @PreDestroy
-> DisposableBean.destroy()
-> @Bean(destroyMethod)
示例:
@PreDestroy
public void shutdown() {
scheduler.shutdown();
}
Spring Boot 收到 SIGTERM 后会关闭上下文。若 Java 进程没有收到信号,例如启动脚本没有 exec,销毁回调不会执行。
3.8 作用域
| 作用域 | 生命周期 |
|---|---|
| singleton | 容器内一个实例 |
| prototype | 每次获取创建新实例 |
| request | HTTP 请求内 |
| session | HTTP 会话内 |
| application | ServletContext 内 |
| websocket | WebSocket 会话内 |
singleton Bean 注入 prototype Bean 时,只会注入一次。需要每次新对象时可用:
@Component
public class TaskFactory {
private final ObjectProvider<TaskWorker> workers;
public TaskFactory(ObjectProvider<TaskWorker> workers) {
this.workers = workers;
}
public TaskWorker newWorker() {
return workers.getObject();
}
}
3.9 代理对生命周期的影响
如果 Bean 需要 AOP,容器最终放入单例池的可能是代理对象。
目标对象
-> 属性填充
-> 初始化
-> AbstractAutoProxyCreator
-> 代理对象
-> 容器对外暴露代理
常见影响:
- 注入类型可能是代理类型;
@Transactional自调用失效;- final 类或方法代理失败;
- CGLIB 代理需要可访问构造器;
- 在构造器中使用依赖可能拿到 null。
3.10 初始化中的阻塞问题
错误做法:
@PostConstruct
public void init() {
// 远程加载大配置,可能耗时 30s
remoteConfig.loadAll();
}
后果:
- 应用启动慢;
- 健康检查误判;
- 发布滚动阻塞;
- 远程故障导致启动失败。
建议:
- 启动只加载必要数据;
- 异步预热后台执行;
- 使用就绪探针区分启动和可服务;
- 外部调用设置超时和重试;
- 首次加载失败允许降级。
3.11 调试生命周期
开启 Bean 创建日志:
logging.level.org.springframework.beans.factory=DEBUG
logging.level.org.springframework.context=DEBUG
断点位置:
| 问题 | 断点 |
|---|---|
| Bean 未创建 | AbstractBeanFactory.getBean |
| 依赖注入失败 | AbstractAutowireCapableBeanFactory.populateBean |
| 初始化失败 | initializeBean |
| AOP 没生效 | AbstractAutoProxyCreator |
| 循环依赖 | DefaultSingletonBeanRegistry |
也可以自定义处理器打印关键阶段:
@Component
public class BeanLifecycleLogger implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (beanName.startsWith("order")) {
System.out.println("before init: " + beanName);
}
return bean;
}
}
本章小结
Bean 生命周期由实例化、属性填充、Aware 回调、初始化前后处理、初始化方法、代理生成和销毁回调组成。BeanPostProcessor 是 Spring 扩展体系的核心,也是自动注入和 AOP 的实现基础。业务代码应优先使用清晰的构造器注入和 @PostConstruct,避免初始化阶段做长时间阻塞操作。
思考题
@PostConstruct和afterPropertiesSet谁先执行?- BeanPostProcessor 能否修改 BeanDefinition?
- singleton 注入 prototype 为什么只创建一次?
- 为什么初始化方法中不适合调用慢速远程服务?
- AOP 代理是在生命周期哪个阶段生成的?