主题
Spring IOC 容器初始化与循环依赖
一句话机制
Spring 通过 AbstractApplicationContext.refresh() 这一个方法完成容器的全部启动工作;Bean 的创建与循环依赖的化解,都发生在这套流程里的 finishBeanFactoryInitialization 阶段,靠三级缓存提前暴露"半成品 Bean"的引用(必要时是 AOP 代理)来打破闭环。
容器初始化主体流程(refresh 12 步)
prepareRefresh()— 刷新前预处理(启动时间、环境校验)obtainFreshBeanFactory()— 拿到DefaultListableBeanFactory,加载并注册BeanDefinition到BeanDefinitionRegistry(本质是一个Map<beanId, BeanDefinition>)prepareBeanFactory()— 给 BeanFactory 做基础设置(类加载器等)postProcessBeanFactory()— 留给子类的后置钩子invokeBeanFactoryPostProcessors()— 实例化并调用BeanFactoryPostProcessorregisterBeanPostProcessors()— 注册BeanPostProcessor(创建 Bean 前后执行)initMessageSource()— 国际化initApplicationEventMulticaster()— 事件派发器onRefresh()— 子类自定义逻辑(如内嵌 Web 服务器)registerListeners()— 注册ApplicationListenerfinishBeanFactoryInitialization()— 实例化所有非懒加载单例 Bean(含属性填充、初始化、后置处理)finishRefresh()— 发布ContextRefreshedEvent
关键心智模型:容器 ≠ 一个 Map。Map(单例池
singletonObjects)只是容器的一个成员;容器是一组组件(BeanFactory、单例池、BeanPostProcessor…)加它们之间的协作流程。
Bean 生命周期(finishBeanFactoryInitialization 内部 7 步)
- 实例化(构造函数,未设属性)
- 属性填充(populateBean)
- 若实现 Aware 接口,调用对应方法
BeanPostProcessor.postProcessBeforeInitialization()InitializingBean.afterPropertiesSet()- 自定义
init-method BeanPostProcessor.postProcessAfterInitialization()
循环依赖
- 能解决哪种:
set/字段注入(setter)方式的循环依赖。 - 解决不了哪种:构造器注入的循环依赖(实例化前就需要对方,来不及提前暴露)。
- 解法:三级缓存(见下)。
三级缓存数据结构
| 级别 | 字段 | 内容 |
|---|---|---|
| 一级 | singletonObjects | 完整版 Bean(初始化完成) |
| 二级 | earlySingletonObjects | 已实例化但未属性赋值的 Bean(或代理) |
| 三级 | singletonFactories | 能生成该 Bean 的 ObjectFactory(可产出代理) |
为什么是三级而不是两级?
两级缓存足以解决"原始对象"的循环依赖,但无法处理"循环依赖 + AOP"同时出现:若 A↔B 且 A 配了 AOP,我们真正要拿到的是 A 的代理对象。只有三级缓存用 ObjectFactory(即 getEarlyBeanReference)在需要时才决定产出原始对象还是代理,从而保证 B 注入的 A 引用与最终容器里的 A 是同一个代理对象。
关键源码路径(A↔B 场景)
- 创建 A:
doCreateBean→createBeanInstance(仅构造)→addSingletonFactory把 A 的工厂放进三级缓存 populateBean(A)发现要注入 B →getBean(B)- 创建 B 同理,B 也进三级缓存;
populateBean(B)要注入 A →getSingleton("A", allowEarlyReference=true) - 一级、二级都查不到 → 从三级缓存的工厂取
getEarlyBeanReference("A")→ 产出 A 的(代理)对象放入二级缓存、从三级缓存移除 - B 属性填充完成 →
getSingleton("B")把完整 B 放入一级缓存、清理二/三级 - 回到 A 的
populateBean:此时从二级缓存拿到代理后的 A,注入给 B 的引用 ✓;A 继续完成初始化,最终addSingleton把完整 A(就是那个代理)放入一级缓存
不变量:最终从容器中拿到的 A、B 互相引用的都是同一份对象(A 为代理时,B 持有的是同一代理)。
常见误解
- "循环依赖靠两级缓存就够了" —— 错,遇到 AOP 会拿到不一致的对象。
- "容器就是个 Map" —— 错,Map 只是单例池。
- "构造器循环依赖 Spring 能解" —— 错,构造器循环依赖直接抛
BeanCurrentlyInCreationException。
关联
- 上游:Spring容器初始化主体流程 · Spring是如何解决循环依赖
- 同域:[Spring AOP与声明式事务](./Spring AOP与声明式事务)(AOP 代理正是三级缓存里
getEarlyBeanReference的产物) - 域地图:Java后端