跳到正文
Joeplover
学习笔记·2026-07-09·约 5 分钟阅读

Spring Bean 生命周期:从 new 到一级缓存的完整链路,以及三级缓存为什么需要第三级

Bean 的生命周期拆成 10 步,三级缓存解决的是 AOP + 循环依赖的交叉问题。从头到尾画一遍完整流程。

代码流程图,Spring 容器概念示意图

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 步
BeanPostProcessorSpring 预留的"拦截器",AOP 代理在第 8 步生成
一级缓存成品 Bean 的 ConcurrentHashMap
二级缓存被提前拿走的半成品
三级缓存存 Lambda 回调,延迟决定"要不要生成代理"
为什么要三级没有第三级,循环依赖 + AOP 时 B 会拿到未代理的 A

整个三级缓存的设计动机,就是解决一个交叉问题:AOP 代理在第 8 步才生成,但循环依赖在第 3 步就可能发生。