Spring Bean 生命周期:从 new 到一级缓存的完整链路,以及三级缓存为什么需要第三级
Bean 的生命周期拆成 10 步,三级缓存解决的是 AOP + 循环依赖的交叉问题。从头到尾画一遍完整流程。
Spring Bean 生命周期:从 new 到一级缓存的完整链路
为什么绕不开 Bean
很多 Spring 初学者把 Bean 等价于"用 new 创建的对象",学完 IOC 觉得"不就是反射 newInstance 嘛"。这个理解离真相还差 9 步。
Bean 和普通对象的区别只有一句话:Bean 是被 Spring 容器接管了全生命周期的对象。
// 普通对象
UserService userService = new UserService(); // 你 new,你用,GC 回收
// Bean
@Component
public class UserService {} // Spring new,Spring 注入依赖,
// Spring 调初始化,Spring 调销毁
接管的不只是"创建"这一步,而是从出生到死亡的一整套流程。
第一站:10 步完整生命周期
很多人背的是 7 步(new → 依赖注入 → beforeInit → init → afterInit → 使用 → 销毁),但漏掉了中间 3 个 Aware 接口回调。
完整 10 步:
① new — 反射调用构造函数,堆里出现一个原始对象
② 依赖注入(populateBean) — 填充 @Autowired 等字段
③ BeanNameAware — Spring 告诉 Bean:"你叫 userService"
④ BeanClassLoaderAware — Spring 告诉 Bean:"你的类加载器是这个"
⑤ BeanFactoryAware — Spring 告诉 Bean:"你的工厂是这个"
⑥ postProcessBeforeInitialization — 初始化前,你可以替换或修改这个 Bean
⑦ init — @PostConstruct 或 InitializingBean.afterPropertiesSet()
⑧ postProcessAfterInitialization — 初始化后,AOP 代理就在这里生成
⑨ 使用 — 你的业务代码在调它
⑩ 销毁 — @PreDestroy 或 DisposableBean.destroy()
Aware 接口的作用是让 Bean 意识到自己在容器中的身份。比如:
@Component
public class UserService implements BeanNameAware {
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name; // Spring 在第三步注入 "userService"
}
}
没有这一步,Bean 就是"盲人"——它不知道自己在容器里叫什么,也不知道工厂在哪。
第二站:BeanPostProcessor —— 拦截每一次 Bean 创建
10 步中第 ⑥ 和 ⑧ 是最有威力的两个扩展点:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName);
Object postProcessAfterInitialization(Object bean, String beanName);
}
Spring 内部有很多内置的 BeanPostProcessor。最典型的一个是 AOP 的代理生成器:
@Component
public class AopProxyProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (hasTransactionalAnnotation(bean)) {
return createProxy(bean); // 返回代理对象,替换原始对象
}
return bean; // 不需要代理,原样返回
}
}
所以第 8 步之后,singletonObjects 里存的可能已经不是原始对象了。
第三站:一级缓存装不下所有情况
Spring 用一个 ConcurrentHashMap 作为一级缓存保存成品 Bean:
// DefaultSingletonBeanRegistry 里
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
但只用一个 Map 会出现一个经典问题:
循环依赖
@Component public class A {
@Autowired private B b;
}
@Component public class B {
@Autowired private A a;
}
如果 A 和 B 各存各的一级缓存,A 创建时 B 还没创建完,B 创建时 A 还没创建完——死锁。
为什么不能用两级缓存
两级缓存(成品 + 半成品)能解决简单的循环依赖:
new A → 放半成品池 → 注入发现要 B → new B → 从半成品池拿到 A → B 完成 → A 从半成品池挪到成品池
但遇到 AOP 就出问题了。
AOP 代理是在第 8 步(postProcessAfterInitialization)生成的。如果 A 需要 @Transactional:
new A → 放半成品池 → B 来拿 A → B 拿到的是原始 A
→ 原始 A 被 B 引用死了
→ A 走完生命周期 → 生成代理对象放进成品池
→ B 手里拿的始终是原始 A,事务不生效
第三级缓存的设计
第三级缓存存的不再是对象,而是一个回调函数:
// 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 在 new 完之后立即放入
singletonFactories.put("a", () -> getEarlyBeanReference("a", aBean));
getEarlyBeanReference 做的事:判断这个 Bean 是否需要 AOP,如果需要就生成代理对象返回,不需要就返回原始对象。
这个回调只有在"有人提前来拿"时才触发。没有循环依赖,这个 Lambda 永远不会被执行。
第四站:完整流程推演
假设 A 没有 @Transactional,B 有 @Transactional。
① new A() → 原始 A 对象
② A 存入三级缓存 → () -> getEarlyReference(A)
③ populateBean A → 需要 B → 创建 B
──────────
④ new B() → 原始 B 对象
⑤ B 存入三级缓存 → () -> getEarlyReference(B)
⑥ populateBean B → 需要 A
⑦ 一级没有 → 二级没有 → 三级有 A
执行 A 的回调 → A 无 @Transactional → 原始 A
原始 A 移到二级缓存
⑧ B 拿到原始 A,注入完成
⑨ B 执行前 6 步 → beforeInit → init → afterInit
★ afterInit 的 BeanPostProcessor 发现 @Transactional
→ 生成 B 的代理对象
⑩ B 代理对象 → 存入一级缓存 ← B 完成
──────────
⑪ 回到 A,从一级缓存拿到 B 的代理对象并注入
⑫ A 执行前 6 步 → beforeInit → init → afterInit
无 @Transactional → 不需要代理
⑬ A 从二级缓存 → 移动到一级缓存 ← A 完成
最终一级缓存里: A 是原始对象,B 是代理对象。
总结
| 概念 | 一句话 |
|---|---|
| Bean 生命周期 | 10 步,Aware 回调是容易被漏掉的 3 步 |
| BeanPostProcessor | Spring 预留的"拦截器",AOP 代理在第 8 步生成 |
| 一级缓存 | 成品 Bean 的 ConcurrentHashMap |
| 二级缓存 | 被提前拿走的半成品 |
| 三级缓存 | 存 Lambda 回调,延迟决定"要不要生成代理" |
| 为什么要三级 | 没有第三级,循环依赖 + AOP 时 B 会拿到未代理的 A |
整个三级缓存的设计动机,就是解决一个交叉问题:AOP 代理在第 8 步才生成,但循环依赖在第 3 步就可能发生。