Spring三级缓存机制解析与循环依赖处理 1. Spring 三级缓存机制深度解析在Spring框架的核心设计中循环依赖处理一直是个让开发者又爱又恨的话题。当Bean A依赖Bean B而Bean B又反过来依赖Bean A时传统依赖注入模式会陷入死循环。Spring通过独创的三级缓存机制优雅地解决了这个问题其设计之精妙堪称框架设计的典范。我曾在多个大型项目中处理过因循环依赖导致的启动异常发现理解三级缓存的工作原理不仅能帮助排查问题更能深入掌握Spring容器的运作机制。本文将结合Spring 5.3源码拆解三级缓存如何像交通协管员一样指挥Bean的创建流程避免创建死锁的发生。2. 循环依赖的本质与破解思路2.1 循环依赖的典型场景考虑以下代码片段Service public class ServiceA { Autowired private ServiceB serviceB; } Service public class ServiceB { Autowired private ServiceA serviceA; }当Spring容器启动时它会尝试创建ServiceA实例 → 发现需要注入ServiceB转去创建ServiceB实例 → 发现需要注入ServiceA又回到ServiceA的创建... 形成无限递归2.2 三级缓存的破局之道Spring的解决方案是引入三个层次的缓存一级缓存singletonObjects存放完全初始化好的Bean二级缓存earlySingletonObjects存放原始Bean对象已实例化但未填充属性三级缓存singletonFactories存放Bean工厂对象这种分层设计允许Spring在Bean未完全初始化时就能提供它的引用给其他Bean使用打破循环链条。就像建筑工地中虽然大楼还未完工但可以先提供设计图纸供其他施工方参考。3. 三级缓存运作全流程剖析3.1 Bean创建的关键步骤以ServiceA和ServiceB的循环依赖为例详细流程如下开始创建ServiceA实例化ServiceA调用构造函数将ServiceA的ObjectFactory放入三级缓存addSingletonFactory(beanName, () - getEarlyBeanReference(beanName, mbd, bean));填充ServiceA属性发现需要注入ServiceB检查一级缓存 → 无检查二级缓存 → 无检查三级缓存 → 无因为ServiceB还未开始创建开始创建ServiceB实例化ServiceB将ServiceB的ObjectFactory放入三级缓存填充ServiceB属性发现需要注入ServiceA检查一级缓存 → 无检查二级缓存 → 无检查三级缓存 → 找到ServiceA的ObjectFactory通过工厂获取ServiceA的早期引用可能经过AOP代理将ServiceA的代理对象放入二级缓存并从三级缓存移除完成ServiceB初始化属性注入完成此时ServiceB持有ServiceA的代理初始化后置处理器将完整ServiceB放入一级缓存回到ServiceA的属性注入现在可以获取到完整的ServiceB实例完成ServiceA的属性注入执行初始化方法将完整ServiceA放入一级缓存3.2 缓存状态变化图示操作阶段一级缓存二级缓存三级缓存初始状态空空空创建ServiceA实例后空空ServiceA的ObjectFactory创建ServiceB实例后空空ServiceAServiceB的工厂ServiceB注入ServiceA时空ServiceA的早期引用ServiceB的工厂ServiceB创建完成后ServiceBServiceA的早期引用空ServiceA创建完成后ServiceAServiceB空空4. 源码级关键实现解析4.1 DefaultSingletonBeanRegistry这是三级缓存的核心实现类关键字段包括// 一级缓存完全初始化好的单例Bean private final MapString, Object singletonObjects new ConcurrentHashMap(256); // 二级缓存提前曝光的原始Bean private final MapString, Object earlySingletonObjects new ConcurrentHashMap(16); // 三级缓存单例工厂对象 private final MapString, ObjectFactory? singletonFactories new HashMap(16);4.2 getSingleton() 方法逻辑获取Bean的核心逻辑如下protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 1. 检查一级缓存 Object singletonObject this.singletonObjects.get(beanName); if (singletonObject null isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 2. 检查二级缓存 singletonObject this.earlySingletonObjects.get(beanName); if (singletonObject null allowEarlyReference) { // 3. 检查三级缓存 ObjectFactory? singletonFactory this.singletonFactories.get(beanName); if (singletonFactory ! null) { // 通过工厂获取早期引用 singletonObject singletonFactory.getObject(); // 升级到二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }4.3 AOP代理的特殊处理当存在AOP代理时三级缓存中的ObjectFactory会通过SmartInstantiationAwareBeanPostProcessor提前生成代理对象。这是通过AbstractAutoProxyCreator实现的public Object getEarlyBeanReference(Object bean, String beanName) { // 如果需要代理则在此处创建代理对象 return wrapIfNecessary(bean, beanName); }5. 实践中的典型问题与解决方案5.1 构造器循环依赖无解三级缓存只能解决字段注入(setter注入)的循环依赖。如果使用构造器注入Service public class ServiceA { private final ServiceB serviceB; public ServiceA(ServiceB serviceB) { this.serviceB serviceB; } } Service public class ServiceB { private final ServiceA serviceA; public ServiceB(ServiceA serviceA) { this.serviceA serviceA; } }Spring会直接抛出BeanCurrentlyInCreationException因为创建ServiceA需要先有ServiceB创建ServiceB又需要先有ServiceA无法通过提前暴露引用来打破循环解决方案重构代码避免构造器循环依赖或改用Lazy延迟加载5.2 prototype作用域的循环依赖Spring不会缓存prototype作用域的Bean因此无法解决其循环依赖Error: Requested bean is currently in creation: Is there an unresolvable circular reference?解决方案改为singleton作用域或使用Lazy延迟注入5.3 异步初始化导致的问题当使用Async时代理对象的创建时机可能打乱三级缓存的正常工作流程Service public class ServiceA { Autowired private ServiceB serviceB; Async public void asyncMethod() { ... } }解决方案确保异步方法所在的Bean不参与复杂依赖链或显式配置代理模式6. 性能优化与最佳实践6.1 缓存配置调优对于大型应用可以调整缓存初始容量减少扩容开销// 在自定义BeanFactoryPostProcessor中设置 ((DefaultListableBeanFactory)beanFactory).setSingletonCacheCapacity(1024);6.2 循环依赖检测工具使用Spring的BeanCurrentlyInCreationException分析工具快速定位问题try { applicationContext.getBean(problematicBean); } catch (BeanCurrentlyInCreationException ex) { System.out.println(循环依赖链 ex.getBeanCurrentlyInCreationException()); }6.3 架构设计建议层级清晰化遵循上层依赖下层原则Controller → Service → Repository避免同层之间相互依赖接口隔离通过接口解耦具体实现Service public class OrderService implements IOrderService { Autowired private IPaymentService paymentService; } Service public class PaymentService implements IPaymentService { Autowired private IInventoryService inventoryService; }事件驱动用ApplicationEvent替代直接调用// 订单创建后发布事件 applicationContext.publishEvent(new OrderCreatedEvent(this, order)); // 库存服务监听事件 EventListener public void handleOrderCreated(OrderCreatedEvent event) { // 减库存逻辑 }7. 三级缓存的演进与替代方案7.1 Spring早期版本的实现在Spring 2.x时代仅使用二级缓存一级缓存完整Bean二级缓存原始Bean 这种设计无法处理AOP代理的情况导致后续引入了三级缓存。7.2 其他IoC容器的解决方案Guice默认不支持循环依赖需通过Provider延迟获取public class ServiceA { private final ProviderServiceB serviceB; Inject public ServiceA(ProviderServiceB serviceB) { this.serviceB serviceB; } }Dagger编译时检测循环依赖直接报错Micronaut通过编译时AOP避免运行时代理简化依赖处理7.3 Spring Reactive的挑战在WebFlux等响应式编程模型中传统的三级缓存机制面临新挑战Bean可能是惰性初始化的依赖关系在订阅时才实际建立需要新的解决方案来处理响应式组件的循环依赖理解三级缓存机制的价值不仅在于解决具体问题更在于领悟框架设计中的权衡艺术。Spring开发者需要明白三级缓存是手段而非目的真正的优雅设计应当尽可能避免复杂的循环依赖。当你的Bean关系网变得过于复杂时这往往是架构需要重构的信号而不是强行依赖框架特性的理由。