在 Java 后端开发中Spring 的 IoC 容器就像是一个庞大且精密的全自动化黑灯工厂。我们只需要在图纸上标明Component或Service剩下的全交给 Spring。但作为一名合格的工程师绝不能仅仅满足于当一个“调包侠”。今天我们就深入这座工厂的内部顺着流水线彻底扒开Spring Bean 的生命周期并直击那个让无数初学者头疼的终极难题循环依赖与三级缓存。一、 流水线纪实Bean 的四大核心生命周期如果把创建一个完整的 Bean 比作制造一辆高配跑车那么 Spring 容器的流水线严格遵循以下四个宏观步骤1. 实例化 (Instantiation) —— 打造车架骨骼这是流水线的第一步。Spring 扫描到你的类后会利用 Java 的反射机制调用类的构造方法在堆内存中开辟一块空间生生“捏”出一个原始对象。状态此时的对象是一个极度纯粹的“半成品”。它虽然存在于内存中但里面的属性比如Autowired标注的其他 Service全都是null。2. 属性注入 (Populate Properties) —— 安装发动机与零件车架子搭好了接下来就是组装。Spring 会检查这个半成品对象内部有哪些依赖项然后去 IoC 容器里把其他对应的 Bean 找出来通过 Setter 方法或直接反射字段强行将它们“塞”进当前对象的属性中。3. 初始化 (Initialization) —— 质检与涡轮增压 (AOP)零件装完车子基本成型但还不算完工。这一步是 Spring 留给开发者的扩展接口各种回调执行比如带有PostConstruct注解的初始化方法会在这里被触发执行一些数据预热逻辑。核心魔法 (AOP)这是极其关键的一环如果这个 Bean 配置了切面比如Transactional事务Spring 会在这一步通过BeanPostProcessor后置处理器利用 JDK 动态代理或 CGLIB为其生成一个代理对象Proxy。最终交到我们手里的其实是这个被强化过的代理车。4. 销毁 (Destruction) —— 报废回收当 Spring 容器关闭时流水线停工。Spring 会调用带有PreDestroy注解的方法或者实现了DisposableBean接口的方法优雅地释放数据库连接、关闭线程池等资源。二、 架构师的梦魇循环依赖的“死锁”悖论按照上述流水线正常的 Bean 创建如丝般顺滑。但现实业务中我们经常会遇到一种极其尴尬的场景循环依赖。假设我们有ServiceA和ServiceBServiceA内部Autowired了ServiceB。ServiceB内部又Autowired了ServiceA。灾难发生了流水线开始制造 A实例化 A半成品 - 准备给 A 注入属性 B。发现 B 还没造出来停下 A 的流水线去造 B。流水线开始制造 B实例化 B半成品 - 准备给 B 注入属性 A。发现 A 也没造完还在等 B停下 B 的流水线去等 A。这就像是“先有鸡还是先有蛋”的死循环如果没有任何干预程序将直接抛出BeanCurrentlyInCreationException异常。三、 破局之术三级缓存与半成品暴露为了打破这种“死锁”Spring 引入了神级设计——三级缓存Three-Level Cache。本质上这就是三个大 Map。一级缓存 (singletonObjects)存放完全初始化好的成品 Bean。也就是我们平时getBean()拿到的最终对象。二级缓存 (earlySingletonObjects)存放提早暴露的半成品 Bean已经实例化但还没注入属性。三级缓存 (singletonFactories)存放Bean 的工厂对象ObjectFactory这是一个生成引用的 Lambda 表达式。见证奇迹的时刻缓存是如何解开循环依赖的让我们重新推演 A 和 B 的制造过程看看 Spring 是如何利用缓存巧妙破局的A 的实例化Springnew出了 A 的半成品。【核心转折】Spring 没有急着去注入属性而是先心急地把 A 包装成一个ObjectFactory工厂函数塞进三级缓存。这相当于提前向世界宣布“A 已经有个胚子了大家谁急用可以先拿去凑合”A 注入 BA 开始属性注入发现需要 B去一级缓存找没有。触发 B 的创建。B 的创建同样B 实例化成半成品把自己的工厂函数塞进三级缓存。然后 B 准备注入属性 A。B 寻找 A破局点B 去一级缓存找 A没找到A 是半成品。B 去二级缓存找 A没找到。B 跑到三级缓存找到了 A 留下的ObjectFactoryB 调用这个工厂方法拿到了 A 的早期引用并顺手把 A 升级放入二级缓存同时清空三级缓存中的 A。B 成功出厂B 顺利把“半成品 A”注入到了自己的属性中。B 继续执行初始化彻底完工被放入一级缓存。A 恢复生产B 造好了卡在流水线上的 A 终于拿到了完整的 B将其注入自己的属性。A 也继续执行初始化彻底完工从二级缓存升级进入一级缓存。至此死锁解开灵魂拷问为什么要用“三级”缓存二级不够吗如果你仔细思考会发现只用两级缓存也能存放“半成品”为什么 Spring 非要搞一个存ObjectFactory的三级缓存呢答案是为了兼容我们在第一部分提到的AOP 动态代理。按照正常的生命周期AOP 代理对象是在第三步初始化时才生成的。但循环依赖发生时B 需要提前拿到 A 的引用。如果 A 配置了事务处理需要被代理B 拿到的绝对不能是 A 的原始对象而必须是 A 的代理对象三级缓存里存的那个ObjectFactory就是用来处理这个特殊情况的。它内部包含了一个逻辑如果 A 需要被 AOP 增强这个工厂方法就会被触发强行提前在属性注入之前生成 A 的代理对象并返回给 B如果不需要增强就直接返回原始对象。结语从一行简单的Autowired到背后精密的四段式生命周期再到解决循环依赖的三级缓存。Spring 用极其优雅的代码结构向我们展示了什么是顶级的系统解耦与状态机管理。记住缓存的本质是空间换时间而三级缓存的本质是利用“提前暴露”打破因果死锁并巧妙兼顾了 AOP 的侵入逻辑。搞懂了这套机制Spring 的底层源码对你而言将不再有秘密。