Spring三级缓存解决循环依赖的解析过程

一、循环依赖场景

假设存在两个bean的相互依赖:

@component
public class servicea {
    @autowired
    private serviceb serviceb;
}

@component
public class serviceb {
    @autowired
    private servicea servicea;
}

二、三级缓存定义

defaultsingletonbeanregistry 中定义:

// 一级缓存:完整bean(k:bean名称 v:实例化+初始化完成的bean)
private final map singletonobjects = new concurrenthashmap<>(256);

// 二级缓存:早期暴露对象(k:bean名称 v:未完成属性注入的原始bean)
private final map earlysingletonobjects = new hashmap<>(16);

// 三级缓存:对象工厂(k:bean名称 v:objectfactory)
private final map> singletonfactories = new hashmap<>(16);

三、解决流程(以servicea和serviceb为例)

1. 创建servicea

sequencediagram
    participant container
    participant cache1 as singletonobjects
    participant cache2 as earlysingletonobjects
    participant cache3 as singletonfactories
    
    container->>cache1: 检查servicea是否存在
    cache1-->>container: 不存在
    container->>container: 实例化servicea(构造器调用)
    container->>cache3: 添加servicea的objectfactory
    container->>container: 开始属性注入(需要serviceb)

关键代码段

// abstractautowirecapablebeanfactory#docreatebean
addsingletonfactory(beanname, () -> getearlybeanreference(beanname, mbd, bean));

2. 发现需要serviceb

sequencediagram
    participant container
    participant cache1
    participant cache2
    participant cache3
    
    container->>cache1: 检查serviceb是否存在
    cache1-->>container: 不存在
    container->>container: 实例化serviceb(构造器调用)
    container->>cache3: 添加serviceb的objectfactory
    container->>container: 开始属性注入(需要servicea)

3. 解决serviceb对servicea的依赖

sequencediagram
    participant container
    participant cache1
    participant cache2
    participant cache3
    
    container->>cache1: 查找servicea
    cache1-->>container: 不存在
    container->>cache2: 查找servicea
    cache2-->>container: 不存在
    container->>cache3: 获取servicea的objectfactory
    container->>container: 执行getearlybeanreference()
    container->>cache2: 将生成的代理对象存入earlysingletonobjects
    container->>serviceb: 注入servicea的早期引用
    container->>container: 完成serviceb初始化
    container->>cache1: 将serviceb放入singletonobjects

4. 回溯完成servicea初始化

sequencediagram
    participant container
    participant cache1
    participant cache2
    participant cache3
    
    container->>servicea: 注入已初始化的serviceb
    container->>container: 执行servicea的初始化后方法
    container->>cache1: 将servicea放入singletonobjects
    container->>cache2: 移除servicea的早期引用
    container->>cache3: 移除servicea的objectfactory

四、关键机制详解

1. getearlybeanreference() 的核心作用

protected object getearlybeanreference(string beanname, rootbeandefinition mbd, object bean) {
    // 如果有必要,在此处生成代理对象
    if (!mbd.issynthetic() && hasinstantiationawarebeanpostprocessors()) {
        for (beanpostprocessor bp : getbeanpostprocessors()) {
            if (bp instanceof smartinstantiationawarebeanpostprocessor) {
                smartinstantiationawarebeanpostprocessor ibp = 
                    (smartinstantiationawarebeanpostprocessor) bp;
                bean = ibp.getearlybeanreference(exposedobject, beanname);
            }
        }
    }
    return bean;
}

2. 三级缓存必要性分析

缓存级别解决的问题典型场景
singletonfactories处理aop代理的延迟生成需要保证代理对象的单例性
earlysingletonobjects避免重复创建早期引用多个bean依赖同一个未完成初始化的bean
singletonobjects存储最终可用bean正常bean获取

五、设计约束与限制

1.仅支持单例作用域

原型(prototype)作用域的bean无法解决循环依赖,因为spring不缓存原型bean

2.构造器注入限制

如果循环依赖通过构造器注入发生,无法解决(实例化前就需要完成依赖注入)

3.异步初始化风险

使用@async等方法增强的bean可能破坏初始化顺序

六、调试技巧

查看缓存状态

defaultsingletonbeanregistry 类中设置断点:

// 查看三级缓存内容
system.out.println("singletonfactories: " + singletonfactories.keyset());
system.out.println("earlysingletonobjects: " + earlysingletonobjects.keyset());
system.out.println("singletonobjects: " + singletonobjects.keyset());

强制抛出循环依赖异常

在配置类添加:

@bean
public circularreferencesbean circularreferencesbean() {
    return new circularreferencesbean(circularreferencesbean());
}

七、性能优化建议

避免过度使用循环依赖

即使技术可行,也应通过设计模式(如事件驱动)解耦

合理使用@lazy注解

延迟加载非必要依赖:

@autowired
@lazy
private serviceb serviceb;

监控缓存命中率

通过jmx监控 singletonobjectsearlysingletonobjects 的比例

总结

spring的三级缓存机制通过 提前暴露对象引用 + 动态代理生成 的协同设计,在保证单例性的前提下,优雅地解决了循环依赖问题。理解该机制需要重点把握bean生命周期的阶段划分和缓存状态的转换逻辑。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

发布于 2025-05-07 22:15:56
分享
海报
170
上一篇:Gradle下如何搭建SpringCloud分布式环境 下一篇:spring IOC的理解之原理和实现过程
目录

    忘记密码?

    图形验证码