为什么一定能是三级缓存?

news/2025/11/19 0:58:17/文章来源:https://www.cnblogs.com/irobotzz/p/19239938

为什么一定能是三级缓存?

为什么一定能是三级缓存?


一、先定一个最小 Demo 场景

1. A / B 循环依赖 + AOP

// 接口只是为了看得更清晰,可有可无
public interface AService {void foo();
}public interface BService {void bar();
}

两个互相依赖的实现:

@Service
public class AServiceImpl implements AService {@Autowiredprivate BService bService;@Override@Transactional   // 或者你自己写个 @Aspect 切这个方法public void foo() {System.out.println("A.foo");bService.bar();}
}@Service
public class BServiceImpl implements BService {@Autowiredprivate AService aService;@Overridepublic void bar() {System.out.println("B.bar");}
}

一个简单的 AOP 切面(也可以不用写,直接用 @Transactional):

@Aspect
@Component
public class LogAspect {@Before("execution(* com.example.AService.foo(..))")public void beforeFoo() {System.out.println("before A.foo");}
}

重点:

  • A 依赖 B
  • B 依赖 A
  • A 上有事务/切面(会被代理)

这样就能触发:循环依赖 + AOP 代理 + 三级缓存的完整流程。


二、按源码顺序走一遍:getBean → getSingleton → doCreateBean

下面以 getBean("aServiceImpl") 为入口,按 Spring 源码主线(省略细节调用)展开。

第 0 步:Client 调用

AService a = applicationContext.getBean(AService.class);
a.foo();

内部相当于:

AbstractBeanFactory#doGetBean("aServiceImpl", AService.class, ...)

第 1 步:doGetBean – 先尝试从单例缓存拿

伪代码:

Object sharedInstance = getSingleton(beanName);
// ↓ 实际调用的是 DefaultSingletonBeanRegistry#getSingleton(name, true)

getSingleton(beanName, allowEarlyReference=false) 中:

  1. 一级缓存 singletonObjects:还没有 → 返回 null
  2. isSingletonCurrentlyInCreation(beanName) 此时是 false(第一次创建)
  3. 返回 null

于是 doGetBean 知道:需要创建新的单例


第 2 步:标记 “正在创建” + 调 createBean

doGetBean 大致做:

beforeSingletonCreation(beanName); // 标记 inCreation=trueObject beanInstance = createBean(beanName, mbd, args); 
// → AbstractAutowireCapableBeanFactory#doCreateBean

此时:

  • isSingletonCurrentlyInCreation("aServiceImpl") = true
  • 还没有任何缓存命中

第 3 步:doCreateBean – 实例化 A(只构造,不注入)

doCreateBean 里核心几步:

  1. createBeanInstance(beanName, mbd, args)
    👉 通过构造器反射,实例化出 原始的 AServiceImpl 对象 rawA

  2. 把一个生成“早期引用”的工厂丢进三级缓存

    真实代码逻辑大概是:

    if (mbd.isSingleton() && allowCircularReferences && isSingletonCurrentlyInCreation(beanName)) {addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, rawA));
    }
    

    对应到三级缓存:

    singletonFactories.put("aServiceImpl", ObjectFactory@A);
    

    这里没有立刻生成早期引用,也没有做代理,
    只是先注册了一个“懒生成”的工厂。

  3. 接下来准备做属性注入:populateBean(beanName, mbd, beanWrapper)


第 4 步:populateBean A – 发现需要注入 B → getBean("bServiceImpl")

在属性注入阶段,Spring 发现 AServiceImpl 有:

@Autowired
private BService bService;

于是调用:

getBean("bServiceImpl");

这又走回 doGetBean("bServiceImpl") 流程。


第 5 步:创建 B – 前半段流程与 A 一样

对 B 来说,步骤完全类似:

  1. getSingleton("bServiceImpl")
    👉 一级/二级/三级都没东西 → null
  2. beforeSingletonCreation("bServiceImpl") 标记正在创建
  3. doCreateBean("bServiceImpl")
    • createBeanInstance 得到原始的 rawB
    • addSingletonFactory("bServiceImpl", () -> getEarlyBeanReference("bServiceImpl", rawB))
      👉 把 B 的 ObjectFactory 放入三级缓存

然后进入 populateBean("bServiceImpl", ...)


第 6 步:populateBean B – 注入 A → getBean("aServiceImpl")(关键点)

B 有:

@Autowired
private AService aService;

于是调用:

getBean("aServiceImpl");

此时情况已经不同:

  • A 正在创建中(isSingletonCurrentlyInCreation("aServiceImpl") == true
  • 三级缓存里已经有了 aServiceImpl 对应的 ObjectFactory

来看这次 getSingleton("aServiceImpl", allowEarlyReference=true) 的执行过程。


三、关键:第二次 getSingleton("aServiceImpl") 的三级缓存命中过程

源码结构(简化版):

public Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;
}

这次调用时的命中顺序:

  1. singletonObjects.get("aServiceImpl")
    👉 一级缓存:没有(A 还没完全初始化)

  2. isSingletonCurrentlyInCreation("aServiceImpl")
    👉 true(我们在创建 A 时标记过)

  3. earlySingletonObjects.get("aServiceImpl")
    👉 二级缓存:还没有(还没生成过早期引用)

  4. allowEarlyReference == true,于是查三级缓存:

    singletonFactory = singletonFactories.get("aServiceImpl");
    

    👉 命中之前在 doCreateBean(A) 时放进去的 ObjectFactory@A

  5. 调用工厂:singletonFactory.getObject()
    👉 实际调用的是:

    getEarlyBeanReference("aServiceImpl", rawA)
    

    在这个方法里,会遍历所有 SmartInstantiationAwareBeanPostProcessor,其中就包括 AOP 的那个:

    for (SmartInstantiationAwareBeanPostProcessor bp : postProcessors) {exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
    }
    
    • 如果 A 需要被代理(比如有 @Transactional / @Aspect 命中),这里会生成 代理对象 proxyA
    • 如果不需要,则直接返回原始对象 rawA
  6. 拿到早期引用之后:

    earlySingletonObjects.put("aServiceImpl", proxyA);
    singletonFactories.remove("aServiceImpl");
    

    至此:

    • 三级缓存命中 → 产出早期引用(可能是代理)
    • 早期引用被放进 二级缓存
    • 对应的 ObjectFactory 从三级缓存中移除(保证只生成一次)
  7. 返回 proxyA,注入到 BServiceImpl 中的 aService 字段:

    bService.aService = proxyA; // B 拿到的是代理过的 A
    

B 的依赖注入完成,后续 B 走自己的初始化流程,最后放进一级缓存。


四、回到 A:完成初始化并升级到一级缓存

现在回到当初在创建 A 的 doCreateBean("aServiceImpl")

  1. 刚才是 A 在 populateBean 时需要 B,导致递归创建 B;
    B 填好依赖(拿到的是 proxyA)之后,B 完成初始化,进入 singletonObjects["bServiceImpl"]

  2. A 那边 populateBean 继续进行,此时给 A 注入 B:

    aService.bService = bServiceImpl; // 这个 bServiceImpl 中持有的是 proxyA
    
  3. A 完成自己的初始化(initializeBean),再经过一轮 BeanPostProcessor(包括 AOP 的后置处理)。

    • AOP 部分会检测:如果已经是代理了,就不会再次生成新代理(这里用的是 BPP 的机制保证幂等)。
  4. registerSingleton("aServiceImpl", proxyA)
    👉 把最终的 bean(其实就是那个早期生成的 proxyA)放入 一级缓存

    singletonObjects.put("aServiceImpl", proxyA);
    earlySingletonObjects.remove("aServiceImpl");
    

至此,三个缓存的状态变成:

  • singletonObjects["aServiceImpl"] = proxyA
  • singletonObjects["bServiceImpl"] = bServiceImpl

earlySingletonObjectssingletonFactories 中都没有 A/B 相关条目。

循环依赖解决,AOP 代理也统一:

  • 容器里任何地方拿到的 A,最终都是 同一个代理实例 proxyA

五、“三级缓存命中”时序图(A/B + AOP 场景)

下面给你画一个简化的时序图,重点标出三级缓存命中点:

sequenceDiagramparticipant Clientparticipant BeanFactoryparticipant S1 as singletonObjectsparticipant S2 as earlySingletonObjectsparticipant S3 as singletonFactoriesparticipant Araw as raw Aparticipant Braw as raw Bparticipant AOP as AopPostProcessorClient->>BeanFactory: getBean("aServiceImpl")BeanFactory->>S1: get("aServiceImpl")S1-->>BeanFactory: nullBeanFactory->>BeanFactory: beforeSingletonCreation("aServiceImpl")BeanFactory->>BeanFactory: doCreateBean("aServiceImpl")Note right of BeanFactory: createBeanInstance → 得到 raw ABeanFactory->>Araw: instantiateNote right of BeanFactory: addSingletonFactory("aServiceImpl", ObjectFactory@A)BeanFactory->>S3: put("aServiceImpl", ObjectFactory@A)Note right of BeanFactory: populateBean(A) → 需要 BBeanFactory->>BeanFactory: getBean("bServiceImpl")BeanFactory->>S1: get("bServiceImpl")S1-->>BeanFactory: nullBeanFactory->>BeanFactory: beforeSingletonCreation("bServiceImpl")BeanFactory->>BeanFactory: doCreateBean("bServiceImpl")Note right of BeanFactory: createBeanInstance → 得到 raw BBeanFactory->>Braw: instantiateNote right of BeanFactory: addSingletonFactory("bServiceImpl", ObjectFactory@B)BeanFactory->>S3: put("bServiceImpl", ObjectFactory@B)Note right of BeanFactory: populateBean(B) → 需要 ABeanFactory->>BeanFactory: getBean("aServiceImpl") (allowEarlyReference = true)BeanFactory->>S1: get("aServiceImpl")S1-->>BeanFactory: nullBeanFactory->>S2: get("aServiceImpl")S2-->>BeanFactory: nullBeanFactory->>S3: get("aServiceImpl")S3-->>BeanFactory: ObjectFactory@ANote right of BeanFactory: 三级缓存命中 → 调用工厂BeanFactory->>AOP: getEarlyBeanReference("aServiceImpl", raw A)AOP-->>BeanFactory: proxyABeanFactory->>S2: put("aServiceImpl", proxyA)BeanFactory->>S3: remove("aServiceImpl")BeanFactory-->>Braw: 注入 aService = proxyANote right of BeanFactory: B 初始化完成 → 放入一级缓存BeanFactory->>S1: put("bServiceImpl", Braw)BeanFactory->>S3: remove("bServiceImpl")Note right of BeanFactory: 回到 A 的 populateBean → 注入 BBeanFactory-->>Araw: 注入 bService = BrawNote right of BeanFactory: A 初始化完成(再次过 AOP BPP)BeanFactory->>S1: put("aServiceImpl", proxyA)BeanFactory->>S2: remove("aServiceImpl")BeanFactory-->>Client: 返回 proxyA

你可以对照这张图记住三次关键动作:

  1. A 实例化后:
    addSingletonFactory("aServiceImpl", factory) → 放入 三级缓存
  2. B 注入 A 时:
    getSingleton("aServiceImpl", true)
    • 三级缓存命中
    • 工厂调用 → getEarlyBeanReference → 生成 proxyA
    • proxyA 放入 二级缓存,三级缓存条目删除。
  3. A 初始化完成时:
    • singletonObjects.put("aServiceImpl", proxyA) 放入 一级缓存
    • 从二级缓存删除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/969525.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KEYDIY KD B25-3 3-Button Universal Flip Remote Key for Hyundai - 5pcs/lot

Solving Hyundai Key Woes: The KEYDIY KD B25-3 Universal Flip Remote Key Problem: The Frustration of Hyundai Key Replacements For European and American automotive repair shops and car owners, finding a …

linux c 编程 入门

你好!欢迎来到Linux C编程入门的指导。Linux C编程是操作系统开发和系统级编程的重要组成部分,它允许开发者直接操作硬件、管理资源、编写高性能的程序等。一、Linux C编程入门基础 1. C语言基础变量类型:int, char…

linux c 目录 存在

要检查 Linux 系统中某个目录是否存在,可以使用以下几种常用命令: 方法 1:使用 ls 命令 ls -r /path/to/directory-r:递归显示(如果目录下有子目录)。 /path/to/directory:要检查的目录路径。方法 2:使用 test…

2025年吨包厂家联系电话推荐:专业采购指南与名录

在工业包装领域,吨包袋作为重要的集装容器,其质量与供应商选择直接影响企业的物流效率与成本控制。随着2025年制造业的持续发展,寻找可靠的吨包厂家成为众多采购人员的核心需求。本文旨在汇总当前市场上信誉良好、服…

2025年吨包厂家联系电话推荐:高效采购与业务对接指南

在现代物流与包装行业中,吨包袋作为大宗货物运输的重要工具,其质量与供应商的服务直接关系到企业的运营效率与成本控制。随着2025年市场需求的不断增长,选择一家可靠的吨包厂家成为众多企业的迫切需求。本文旨在为广…

2025年弱碱性水品牌联系电话推荐:优质品牌与联系渠道

引言 随着人们对健康饮水意识的不断提高,弱碱性水因其天然矿物质含量和适宜的pH值受到越来越多消费者的关注。为了帮助用户快速找到可靠的弱碱性水品牌联系方式,本文汇总了2025年推荐的弱碱性水品牌联系电话,并提供…

电商知识库概念预备

以下是整合两次回答的 200个二手电商专业词汇,按 商品属性、交易行为、平台规则、物流售后、支付金融、数据运营、细分领域 七大维度分类整理,覆盖奢侈品、3C数码、直播电商等场景: 一、商品属性类(50个)成色描述…

2025年弱碱性水品牌联系电话推荐:精选推荐与使用指南

随着人们对健康饮水的关注度不断提升,弱碱性水因其天然的特性受到越来越多消费者的青睐。寻找可靠且优质的弱碱性水品牌联系方式成为许多人的需求。本文旨在汇总2025年市场上值得信赖的弱碱性水品牌联系电话,为您的选…

2025年富锶水品牌联系电话推荐:优质水源与联系方式

随着人们对健康饮水意识的不断提升,富锶水因其富含对人体有益的矿物质元素而受到越来越多消费者的青睐。无论是家庭日常饮用、办公需求还是户外出行,选择一款优质的富锶水品牌成为许多人的关注焦点。为了帮助大家快速…

2025年蒸汽发生器品牌电话推荐:高效联系与选购指南

随着工业发展和节能需求的提升,蒸汽发生器作为关键设备,在供热、生产等领域发挥着重要作用。许多用户在选择蒸汽发生器品牌时,希望快速联系到可靠供应商,获取专业咨询或定制服务。本文针对2025年的市场需求,汇总了…

2025年蒸汽发生器品牌电话推荐:高效沟通与实用建议

随着工业节能和环保要求的不断提高,蒸汽发生器作为热能设备的重要组成部分,市场需求日益增长。为了帮助用户快速找到可靠的蒸汽发生器品牌及联系方式,本文汇总了2025年推荐的五大品牌电话,并附上详细的使用建议。无…

2025年数码印花厂家联系电话推荐:快速对接生产资源指南

在纺织服装行业快速发展的今天,数码印花技术以其灵活性强、精度高、环保节能等优势,成为面料印花的重要选择。无论是服装品牌、贸易公司还是独立设计师,寻找可靠的数码印花厂家是实现创意设计、保证产品质量的关键环…

2025年吨包厂家联系电话推荐:高效联系与选择指导

对于需要采购吨包或集装袋的企业来说,找到可靠的供应商至关重要。吨包作为大宗货物包装的主要形式,其质量、价格和交货周期直接影响采购成本和运营效率。许多用户在寻找吨包厂家时,往往面临信息分散、联系方式不准确…

2025年数码印花厂家联系电话推荐:专业团队与生产实力

在纺织服装行业快速发展的今天,寻找可靠、高效的数码印花厂家成为许多企业与设计师的核心需求。无论是为了小批量定制、新品打样,还是大规模生产,一个专业的合作伙伴不仅能保障产品质量,更能优化生产流程,节约时间…

2025年11月电磁吸盘厂家排名参考:多维度数据与用户评价汇总

作为工业制造领域的关键设备,电磁吸盘的选择直接影响生产效率和加工精度。许多用户可能是机械加工企业负责人、设备采购专员或生产工程师,他们需要在磨床、铣床等机床上安装电磁吸盘以固定导磁性工件。这类用户通常面…

2025年富锶水品牌联系电话推荐:实用联系信息汇总

随着人们对健康饮水意识的不断提升,富锶水作为富含多种矿物质元素的天然饮用水,越来越受到消费者的青睐。2025年,市场上涌现出众多富锶水品牌,为帮助大家快速找到可靠的联系方式,本文汇总了当前较具代表性的几家富…

2025年蒸汽发生器品牌电话推荐:高效联系与建议

在工业生产和能源应用领域,蒸汽发生器作为关键设备,其品牌选择与售后服务联系渠道对用户至关重要。随着2025年技术发展和市场需求变化,用户对蒸汽发生器品牌的电话联系方式需求日益增长,尤其是寻求高效、可靠的设备…

2025年11月电磁吸盘厂家排名指南:基于用户需求与行业数据评价

作为工业制造领域的关键设备,电磁吸盘广泛应用于机床加工、物流搬运、冶金等行业,其性能直接影响生产效率和作业安全。许多用户在选购时面临诸多挑战:一是市场厂家众多,资质参差不齐,难以快速识别可靠供应商;二是…

2025年数码印花厂家联系电话推荐:合作前必读联系指南

在纺织服装行业快速发展的今天,数码印花技术以其灵活、高效、高品质的特点,成为众多品牌和贸易公司的首选。无论是进行新品打样还是大批量生产,找到一家技术过硬、产能稳定、服务优质的数码印花厂家至关重要。然而,…

2025年11月工商数据平台深度评测:聚焦用户需求与行业现状

随着企业数字化转型加速,工商数据服务需求显著增长。用户可能包括金融机构风控人员、企业采购部门、投行分析师等,他们需通过权威数据辅助决策,例如信贷审批、供应商筛选或投资尽调。行业现状显示,政策对数据合规性…