目录

一、完整流程图

二、每个阶段实际在干嘛

① 实例化 — Spring 通过反射 new 对象

② 属性注入 — @Autowired/@Value 填值

③ Aware 回调 — Spring 告诉你"你是谁"

④ BeanPostProcessor.Before — 初始化前拦截

⑤ 初始化 — 真正的干活阶段(三步走)

⑥ BeanPostProcessor.After — AOP 代理就在这里!

⑧ 销毁 — 容器关闭时的善后(三步走)

三、默认执行 vs 需要主动配置

🟢 默认执行的(你不写任何代码也会走)

🔵 需要你主动写代码才会触发的

四、初始化前后的 BeanPostProcessor 作用

五、一句话总结全流程

六、面试高频点

七、面试话术

八、验证代码



一、完整流程图



二、每个阶段实际在干嘛

① 实例化 — Spring 通过反射 new 对象

// Spring 做的事:new UserService()
// 这时候所有属性都是 null 或默认值
UserService userService = new UserService();
​
// 打印出来是:
dbUrl = null          ← @Value 还没注入!
maxActive = 0         ← @Value 还没注入!
configLoader = null   ← @Autowired 还没注入!

实际意义: 千万别在构造方法里调用这些属性,会报空指针。


② 属性注入 — @Autowired/@Value 填值

@Autowired private ConfigLoader configLoader;  // 这时候才注入
@Value("${db.url}") private String dbUrl;      // 这时候才注入
​
// 到 @PostConstruct 时,值已经正常了:
dbUrl = jdbc:mysql://localhost:3306/userdb      ← 不再是 null
configLoader = ConfigLoader{version='v2.3.1'...}  ← 不再是 null

实际意义: 你的 @Autowired@Value 都在这里生效。


③ Aware 回调 — Spring 告诉你"你是谁"

setBeanName → "我叫 userService"
setBeanFactory → "我已经能看到容器里有其他 Bean 了"
setApplicationContext → "容器里一共 9 个 Bean"

实际意义:

  • BeanNameAware → 项目中几乎不用

  • ApplicationContextAware → 极少数项目用来手动获取 Bean


④ BeanPostProcessor.Before — 初始化前拦截

实际意义: Spring 自己用它来解析 @Autowired@Value 等注解。


⑤ 初始化 — 真正的干活阶段(三步走)

执行顺序 方式 代码 你要怎么写
@PostConstruct 方法上加注解 方法上加 @PostConstruct
InitializingBean 实现接口重写 afterPropertiesSet() implements InitializingBean
init-method @Bean 里指定方法名 @Bean(initMethod="xxx")

实际用途:

  • 启动资源(创建数据库连接池、启动定时任务)

  • 校验配置(检查必要属性是否都注入了)

  • 预热数据(从数据库加载热点数据到内存)


⑥ BeanPostProcessor.After — AOP 代理就在这里!

实际意义: 如果你给 UserService 加了 @Transactional,这步会返回一个 CGLIB 代理对象,而不是原始对象。这就是为什么 @Transactional 只在通过 Spring 容器获取的 Bean 上生效。


⑧ 销毁 — 容器关闭时的善后(三步走)

执行顺序 方式 代码 你要怎么写
@PreDestroy 方法上加注解 方法上加 @PreDestroy
DisposableBean 实现接口重写 destroy() implements DisposableBean
destroy-method @Bean 里指定方法名 @Bean(destroyMethod="xxx")

实际用途: 关闭线程池、断开数据库连接、保存未处理的消息。


三、默认执行 vs 需要主动配置

🟢 默认执行的(你不写任何代码也会走)

  • 实例化:只要声明了 @Component / @Bean 就会走

  • 属性注入:只要有 @Autowired@Value 就会注入

  • AOP 代理:Spring Boot 默认自带,加 @Transactional 就生效

🔵 需要你主动写代码才会触发的

扩展点 你要怎么写 平时用过吗
BeanNameAware implements BeanNameAware ❌ 几乎不用
ApplicationContextAware implements ApplicationContextAware ⚠️ 极少数项目用
BeanPostProcessor implements BeanPostProcessor 并注册 ❌ 没用过
@PostConstruct 方法上加注解 ✅ 最常用
@PreDestroy 方法上加注解 ⚠️ 偶尔
InitializingBean implements InitializingBean ❌ 很少直接用
initMethod @Bean(initMethod="xxx") ❌ 很少直接用

你日常开发里 99% 的 Bean 生命周期就是:

① 实例化  →  ② 属性注入  →  ③ 就绪
                            ↘ 如果加了 @PostConstruct,中间插一步
                            ↘ 如果加了 @Transactional,最后变成代理对象

四、初始化前后的 BeanPostProcessor 作用

阶段 一句话作用
初始化 属性注入完成后,Bean 还需要"做点什么准备"才能真正干活(启动连接池、校验配置、加载缓存)
初始化前拦截 Spring 在初始化之前插入一个"卡点",让你可以预处理(Spring 自己用它解析 @Autowired
初始化后拦截 Bean 初始化完成后,拦截器可以替换整个对象(AOP 代理就是在这里把原始对象替换成代理对象的)

五、一句话总结全流程

new出来(空的)
  ↓
填依赖(@Autowired生效)
  ↓
告诉我你是谁(Aware)
  ↓
初始化前(BeanPostProcessor ✋)
  ↓
@PostConstruct → 准备资源
afterPropertiesSet → 检查配置
init-method → 加载缓存
  ↓
初始化后(AOP代理就在这里!🤖)
  ↓
=== 可以用了!调业务方法 ===
  ↓
容器关闭 → @PreDestroy → destroy → destroy-method → 释放资源

六、面试高频点

问题 答案
AOP 代理什么时候创建的? BeanPostProcessor.postProcessAfterInitialization()
@Autowired 在哪工作的? 属性注入阶段,由 AutowiredAnnotationBeanPostProcessor 处理
@PostConstruct vs InitializingBean 谁先? @PostConstruct
prototype 会被销毁吗? 不会! Spring 只管理 singleton 的销毁
BeanPostProcessor 可以用几个? 多个,按 Ordered 接口排序执行
Spring Boot 默认用什么代理? CGLIBspring.aop.proxy-target-class=true

七、面试话术

问:Bean 有哪几种初始化方式?执行顺序是什么?

有三种,按顺序执行:

  1. @PostConstruct(注解方式)—— 方法上加注解

  2. InitializingBean(接口方式)—— 实现接口重写 afterPropertiesSet()

  3. init-method(配置方式)—— 在 @Bean(initMethod="xxx") 里指定

其中最常用的是 @PostConstruct,因为它最简单——加个注解就行。 顺序是固定的,由 Spring 源码 AbstractAutowireCapableBeanFactory.doCreateBean() 决定。 实际开发中,90% 的场景只需要用 @PostConstruct 就够了。

问:BeanPostProcessor 的 Before 和 After 有什么区别?

  • Before(初始化前):Spring 自己用它来解析 @Autowired@Value 等注解

  • After(初始化后):这是 AOP 创建代理对象的地方。如果你加了 @Transactional,Spring 在这步把原始 Bean 替换成 CGLIB 代理对象

一句话总结:Before 是给 Spring 自己用的,After 是给 AOP 用的。


八、验证代码

代码项目在本账号的仓库中:bean-init-demo可以直接运行

预期输出如下:

【构造方法】MyService 被 new 出来了(还没注入属性)
【属性注入】name 被设置了:温度监控服务
​
【方式一 · @PostConstruct】我执行了!    ← 第一个执行
【方式二 · InitializingBean】我执行了!  ← 第二个执行
【方式三 · init-method(配置)】我执行了! ← 第三个执行
​
【业务】MyService 在干活,name=温度监控服务
​
【销毁一 · @PreDestroy】我执行了!        ← 第一个销毁
【销毁二 · DisposableBean】我执行了!     ← 第二个销毁
【销毁三 · destroy-method】我执行了!     ← 第三个销毁
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐