937 字
约 3 分钟
7
S07-B02 · Bean 生命周期与作用域
适用读者:分不清
@PostConstruct与afterPropertiesSet的人。
前置:S07-B01。
实测环境:Spring Boot 3.3.6 / JDK 17(2026-11-05)。
覆盖旧文:967(79) 2638(111) 965(69) 964(67) = 326 点。
覆盖扩展:E-S07-003(回调顺序)、E-S07-004(单例线程安全)。
上一篇:S07-B01 · IoC 与容器:Bean 从哪来、谁管它
下一篇:S07-B03 · AOP。
1. 一句话结论
Bean 的一生有固定顺序:构造 → 属性注入 → @PostConstruct → afterPropertiesSet → 使用 → @PreDestroy。记住这个顺序,就能定位"初始化时依赖还没就绪"的 bug。
实测输出(启动时):
[1] 构造器
[2] @PostConstruct
[3] afterPropertiesSet
... Tomcat started on port 19070
... Started App in 8.281 seconds
实测输出(优雅关闭时):
[4] @PreDestroy
2. 完整顺序(E-S07-003)
① 实例化(构造器)
② 属性注入(@Autowired / @Value)
③ BeanNameAware / BeanFactoryAware / ApplicationContextAware
④ BeanPostProcessor.postProcessBeforeInitialization
⑤ @PostConstruct
⑥ InitializingBean.afterPropertiesSet
⑦ 自定义 init-method
⑧ BeanPostProcessor.postProcessAfterInitialization ← AOP 代理在此生成
⑨ 使用
⑩ @PreDestroy → DisposableBean.destroy → 自定义 destroy-method
| 顺序对 | 容易踩的坑 |
|---|---|
| ④ 早于 ⑤ | 在 postProcessBeforeInitialization 里读 @PostConstruct 设置的字段会拿到 null |
| ⑤ 早于 ⑥ | 两个都写时,@PostConstruct 先执行 |
| ⑧ 在最后 | 所以 @PostConstruct 里的 this 是原始对象,不是代理(AOP 失效原因之一) |
| ⑩ 只在容器关闭时触发 | kill -9 不会执行 |
3. 三种初始化方式的取舍
| 方式 | 优点 | 缺点 |
|---|---|---|
@PostConstruct |
标准(jakarta.annotation)、语义清晰 |
无参数、无返回值 |
InitializingBean |
容器接口,能力更强 | 耦合 Spring |
init-method |
XML/@Bean(initMethod) 可配 |
字符串配置,易拼错 |
判据:默认用 @PostConstruct;需要"Spring 特有信息"时才用 InitializingBean。
4. 作用域与线程安全(E-S07-004、A-964)
| 作用域 | 实例数 | 典型用途 |
|---|---|---|
| singleton(默认) | 每容器 1 个 | 无状态服务 |
| prototype | 每次请求新实例 | 有状态对象 |
| request / session | 每请求/会话 | Web 场景 |
| application | 每 ServletContext | 全局配置 |
"单例 Bean 是线程安全的吗?"——不是自动的:
| Bean 状态 | 线程安全 |
|---|---|
| 无状态(只有方法,无成员变量) | ✅ 安全 |
| 有可变成员变量 | ❌ 不安全(多线程共享) |
成员是线程安全对象(如 ConcurrentHashMap) |
⚠️ 取决于用法 |
实测对照(S05-B01):8 线程操作同一个 int 成员 → 丢 37% 更新。单例 + 可变状态 = 同一个问题。
5. 五个误区
| 误区 | 真相 |
|---|---|
"@PostConstruct 在属性注入之前" |
之后(②→⑤) |
| "单例 Bean 天生线程安全" | 只有无状态才安全 |
"@PreDestroy 一定执行" |
只在容器优雅关闭时 |
"prototype 由容器管理销毁" |
不管理(销毁要自己负责) |
"@PostConstruct 里能拿到 AOP 代理" |
拿到的是原始对象(⑧ 还没执行) |
6. 自检
- 完整生命周期顺序(至少列出 6 步)?
@PostConstruct与afterPropertiesSet谁先?- 为什么
@PostConstruct里的this不是代理? - 单例 Bean 什么时候不安全?
prototype作用域的销毁由谁负责?
7. 下一篇
S07-B03 · AOP:切面、代理与失效场景
生命周期讲完,下一篇讲"代理是在哪一步生成的"——以及为什么同类内部调用不走切面(实测有 AOP 日志)。
本篇属于系列《S07 · Spring 框架》。旧文来源:A-967/2638/965/964;扩展点 E-S07-003/004。生命周期回调为 Spring Boot 3.3.6 实测。
S07-B02 · Bean 生命周期与作用域
https://www.clxhxhhr.top/posts/5269/ 评论
0 条
还没有评论,先写一条吧。