Bean生命周期与三级缓存
一、Bean 生命周期全景
1. 实例化(Instantiation)
└─ 构造器创建对象(反射 newInstance),属性都还是默认值
2. 属性填充(Populate)
└─ 依赖注入:@Autowired / @Value / setter 注入,把依赖 Bean 塞进去
3. 初始化前(BeanPostProcessor.postProcessBeforeInitialization)
└─ @PostConstruct 注解的方法在此执行
4. 初始化(InitializingBean.afterPropertiesSet / init-method)
└─ 自定义初始化逻辑(连接池启动、资源预加载)
5. 初始化后(BeanPostProcessor.postProcessAfterInitialization)
└─ **AOP 代理对象在这里生成**——这就是为什么从容器拿到的 Bean 可能是代理
6. 就绪(Ready)
└─ Bean 可用,存在单例池(singletonObjects)中
7. 销毁(DisposableBean.destroy / @PreDestroy)
└─ 容器关闭时调用,释放资源(连接池关闭、线程池 shutdown)
BeanPostProcessor 是 Spring 最关键的扩展点——它包裹了"初始化前后"两个钩子,所有框架级功能(@Autowired、@Transactional、@Async)都是通过自定义 BeanPostProcessor 实现的。
二、三级缓存与循环依赖
问题:A 依赖 B,B 依赖 A——两个 Bean 都等着对方先创建好,传统思路会死锁。
Spring 的答案——三级缓存:
singletonObjects 一级:完全就绪的 Bean(经过实例化→填充→初始化→代理)
earlySingletonObjects 二级:早期引用(已经实例化,但还没填充属性)
singletonFactories 三级:ObjectFactory,能提前暴露"半成品"的引用
A 与 B 互相依赖的解决过程:
- 创建 A → 实例化 A(反射调构造器)→ 把 A 的 ObjectFactory 放进三级缓存("谁要我给谁半成品")
- 填充 A 的属性 → 发现需要 B → 去创建 B
- 创建 B → 实例化 B → 把 B 的 ObjectFactory 放进三级缓存
- 填充 B 的属性 → 发现需要 A → 从三级缓存拿到 A 的半成品引用(此时 A 还没填充属性,但引用已经存在了)
- B 完成初始化 → 放进一级缓存
- 回到 A → 把 B 注入 → A 完成初始化 → 放进一级缓存
三个关键事实:
- 三级缓存只解决单例 setter 注入的循环依赖——构造器注入的循环依赖无法解决(因为实例化这一步就卡住了,根本没机会暴露半成品);
- AOP 代理:三级缓存里 ObjectFactory 调用
getObject()时,如果该 Bean 需要 AOP 代理,会返回代理对象而非原始对象——所以注入的依赖始终是"最终形态"; - 二级缓存的作用:防止每次获取都重新调用 ObjectFactory 生成代理(缓存最终的早期引用)。
三、Bean 作用域
| 作用域 | 含义 | 常见场景 |
|---|---|---|
| singleton | 整个容器一个实例(默认) | 无状态 Bean(Service/DAO) |
| prototype | 每次获取新实例 | 有状态 Bean(每次请求不同数据) |
| request | 每次 HTTP 请求一个实例 | Web 环境下请求级数据 |
| session | 每个 HTTP Session 一个实例 | 用户会话数据 |
prototype 的坑:Spring 不管理 prototype Bean 的完整生命周期——只负责创建,销毁由调用方自己管。因此 singleton Bean 里注入 prototype Bean 时,singleton 只会被注入一次(创建 singleton 时的那次),之后每次调用拿到的都是同一个——这不是"每次新实例",除非用 @Lookup 或 ObjectProvider。
四、高频面试题速答
- Bean 生命周期有哪些阶段? 见第一节全景图,关键点:BeanPostProcessor 的两个钩子、AOP 代理在初始化后生成。
- Spring 如何解决循环依赖? 三级缓存:实例化后暴露半成品引用(ObjectFactory),让依赖方先拿到引用再等填充完成。只支持单例 setter 注入的循环依赖。
- 构造器注入的循环依赖能解决吗? 不能——实例化这一步就卡住了,Bean 还没出生,三级缓存里没有它的半成品。
- prototype 作用域的循环依赖? 同样不能——prototype 不进入三级缓存,Spring 会直接抛 BeanCurrentlyInCreationException。
- 二级缓存为什么存在? 缓存 ObjectFactory 生成的早期引用(包括 AOP 代理),避免重复调用 getObject()。
勾连
- IoC 容器与 BeanFactory 的关系:Spring IOC
- 依赖注入的三种方式与循环依赖的限制:依赖注入 DI
- AOP 代理的生成时机:Spring AOP
⬅️ Spring AOP 🏠 00-Java ➡️ 05-Spring事务管理
💬 评论