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. 自检

  1. 完整生命周期顺序(至少列出 6 步)?
  2. @PostConstruct 与 afterPropertiesSet 谁先?
  3. 为什么 @PostConstruct 里的 this 不是代理?
  4. 单例 Bean 什么时候不安全?
  5. 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/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。