当前位置: 首页 > article >正文

Spring三级缓存解密:循环依赖破局之道

一、循环依赖的致命困境

1.1 典型场景示例

@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

传统初始化流程

  1. 创建ServiceA实例 → 需要注入ServiceB
  2. 创建ServiceB实例 → 需要注入ServiceA
  3. 回到步骤1 → 无限循环

二、三级缓存架构揭秘

2.1 缓存层次结构


缓存层级存储内容生命周期阶段
一级缓存完全初始化好的单例Bean初始化完成
二级缓存完成实例化但未初始化的原始Bean属性填充前
三级缓存Bean的ObjectFactory(工厂对象)实例化后立即存入

三、缓存操作核心流程

3.1 Bean创建流程图解


四、源码级执行分析

4.1 关键方法调用链

// AbstractBeanFactory.doGetBean()
↓ getSingleton(beanName, true) // 尝试从缓存获取
  ↓ getSingleton(beanName, () -> createBean(...)) 
    ↓ createBean() 
      ↓ doCreateBean()
        ↓ addSingletonFactory(beanName, () -> getEarlyBeanReference(...)) // 存入三级缓存
        ↓ populateBean() // 属性注入触发依赖查找

4.2 缓存交互时序

// 首次获取ServiceA
1. getSingleton("serviceA") → null
2. createBean() → 实例化ServiceA
3. addSingletonFactory("serviceA", ObjectFactory)
4. populateBean() → 发现需要serviceB

// 获取ServiceB
5. getSingleton("serviceB") → null
6. createBean() → 实例化ServiceB
7. addSingletonFactory("serviceB", ObjectFactory)
8. populateBean() → 发现需要serviceA

// 再次获取ServiceA
9. getSingleton("serviceA") → 从三级缓存获取ObjectFactory
10. getEarlyBeanReference() → 返回原始对象或代理对象
11. 将serviceA引用存入二级缓存

五、AOP代理的特殊处理

5.1 代理对象生成时机

// AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName, 
                                      RootBeanDefinition mbd, 
                                      Object bean) {
    // 如果存在SmartInstantiationAwareBeanPostProcessor
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = 
                (SmartInstantiationAwareBeanPostProcessor) bp;
            return ibp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}

关键点

  • 三级缓存存储的是ObjectFactory而非原始对象
  • AOP代理在此阶段提前生成
  • 保证最终注入的是同一个代理对象

六、缓存策略的局限性

6.1 无法解决的场景

场景类型示例解决方案
构造器循环依赖new ServiceA(serviceB)使用@Lazy延迟注入
prototype作用域@Scope("prototype")重构代码设计
异步初始化@Async初始化方法调整初始化顺序
非单例Bean非Spring管理的对象保证依赖项为单例

6.2 典型异常分析

// 构造器注入导致的异常信息
BeanCurrentlyInCreationException: 
Error creating bean with name 'serviceA': 
Requested bean is currently in creation: 
Is there an unresolvable circular reference?

破局方案

// 使用@Lazy注解打破循环
public ServiceA(@Lazy ServiceB serviceB) {
    this.serviceB = serviceB;
}

七、生产环境调优建议

7.1 缓存配置参数

# 允许早期引用暴露(默认true)
spring.main.allow-circular-references=true 

# 单例缓存最大数量(默认无限制)
spring.beans.singleton.max-count=10000

7.2 性能监控指标

// 监控缓存命中率
DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) ctx.getBeanFactory();
int singletonCount = registry.getSingletonCount();
int earlySingletonCount = registry.getEarlySingletonReferences().size();

八、现代架构演进

8.1 与Spring Boot的整合


8.2 响应式编程的影响

// Reactor上下文中的延迟注入
@Bean
public ServiceA serviceA(ObjectProvider<ServiceB> serviceBProvider) {
    return new ServiceA(serviceBProvider.getIfAvailable());
}


http://www.kler.cn/a/562014.html

相关文章:

  • H13-821 V3.0 HCIP 华为云服务架构题库
  • 「软件设计模式」命令模式(Command)
  • Crack SmartGit
  • 使用虚拟IP会封号吗?静态IP上网速度会不会比动态IP更快?
  • 《苍穹外卖》电商实战项目(java)知识点整理(P1~P65)【上】
  • 6.将cr打包成网络服务|使用postman进行测试|编写oj_server的服务路由功能(C++)
  • 《白帽子讲Web安全》学习:深入解析Cookie与会话安全
  • 玩机日记 14 飞牛fnOS部署qBittorrent、AList、Jellyfin,实现下载、存取、刮削、观看一体的家庭影音中心
  • 【Python爬虫(55)】Scrapy进阶:深入剖析下载器与下载中间件
  • 物理服务器如何保障数据的安全性?
  • Qt常用控件之单行输入框QLineEdit
  • Vue进阶之AI智能助手项目(四)——ChatGPT的调用和开发
  • 在Linux、Windows和macOS上部署DeepSeek模型的最低配置要求
  • 2021Java面试-基础篇
  • STM32【3】芯片的底层组成概论
  • Kafka可视化工具EFAK(Kafka-eagle)安装部署
  • OpenCV(9):视频处理
  • Linux 第三次脚本作业
  • HBase:大数据时代的“超级数据库”
  • 分布式之Gossip协议