【Spring】三级缓存

目录标题

  • [触发所有未加载的实例a - 开始](#触发所有未加载的实例a - 开始)
  • [getBean( doGetBean) - 获取单例bean](#getBean( doGetBean) - 获取单例bean)
    • [getSingleton() - 获取单例bean](#getSingleton() - 获取单例bean)
    • [createBean(doCreateBean) - 创建bean](#createBean(doCreateBean) - 创建bean)
  • [触发所有未加载的实例a - 结束](#触发所有未加载的实例a - 结束)
  • [触发所有未加载的实例b - 开始](#触发所有未加载的实例b - 开始)
  • [触发所有未加载的实例b - 结束](#触发所有未加载的实例b - 结束)

结合文章:循环依赖
测试代码如下:

java 复制代码
public class A {
    private B b;

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }

    public A() {
        System.out.println("---A created success");
    }
}
java 复制代码
public class B {
    private A a;

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }
    
    public B() {
        System.out.println("---B created success");

    }
}
java 复制代码
public class ClientSpringContainer {


    public static void main(String[] args) {
        sampleDemo();
    }


    private static void sampleDemo() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        A a = context.getBean("a", A.class);
        B b = context.getBean("b", B.class);
    }
}

执行refresh 方法

执行finishBeanFactoryInitialization 方法

执行preInstantiateSingletons 方法

触发所有未加载的实例a - 开始

getBean( doGetBean) - 获取单例bean

实际上就是通过 doGetBean ,先进行 bean-a 的初始化

getSingleton() - 获取单例bean

去缓存查看时候有 bean - a

实际上就是通过双重校验锁,去查看一级缓存中是否有 bean-a 并且没有在创建中 ,所以就返回 null

由于返回了 null ,则 继续往下走,去创建bean-a实例
由于我们 bean-a 是单例的,所以就执行下面的语句块

再进入内部,发现返回的其实就是执行的就是传参的 createBean(beanName, mbd, args)

createBean(doCreateBean) - 创建bean

createBeanInstance - 创建并返回bean

先执行的是 createBeanInstance,里面是通过构造函数去创建一个bean实例

addSingletonFactory -放三级缓存

继续,需要暴露出这个bean-a到 三级缓存中,此时我们是有了bean-a的实例:A@2321

  1. 往三级缓存中放入 a - lamdba@2337
  2. 删除二级缓存

populateBean - 属性设值

  1. 先解析看看bean -a 需要那些属性
  2. 在进行设值
applyPropertyValues - 设值属性

resolveValueIfNecessary 实际上调用了 resolveReference

resolveReference 也是通过beanFactory 中获取bean


初始化b开始

而由于此时工厂里面没有bean-b,没有进行初始化

实际上是调用了doGetBean,又开始了 实例化 bean-b的过程,getBean

从缓存中获取b

查看一级缓存中是否存在bean-b,返回null

创建bean-b实例

相当于重复了createBean

由于我们在前一步返回的是null,所以就去执行else语句块的内容

常见的是单例bean,进入方法,执行的是createBean方法


调用了doCreateBean方法

createBeanInstance , B@2627

提前暴露bean-b

addSingletonFactory

三级缓存中放入 b - lambda@2644

此处我们也可以看到在三级缓存中有两个

b 属性设值

调用 populateBean

applyPropertyValues

这里我们可以看到 b 是需要a的(符合我们 前面的需求:a b 互相引用)

applyPropertyValues 里面又调用了 resolveValueIfNecessary

resolveValueIfNecessary 又调用了 resolveReference

resolveReference 调用了 beanFactory.getBean,getBean又是通过doGetBean去获取


先从一级缓存中去获取bean-a ,返回null

由于一级缓存中没有且a在创建中,执行if语句块

查看二级缓存中是否有a,没有,执行if 语句块

再从三级缓存中去获取a, 这里是能够获取从三级缓存中获取到的点击访问

  1. a ,三级缓存中的实例 lambda@2337 对应的 实例 A@2321
  2. 放入二级缓存中去 A@2321,此时就已经把a放入二级缓存里面了
  3. 删除三级缓存的内容
  4. 返回缓存中的实例 A@2321

由于能够从三级缓存中去获取到半成品a,A@2321

doGetBean 返回 从三级缓存中获取到的A@2321,所以执行if语句快,发现最后返回的是bean,bean是通过getObjectForBeanInstance 去获取的

getObjectForBeanInstance,又去调用了 super.getObjectForBeanInstance

getObjectForBeanInstance 就是返回了 A@2321

doGetBean 结束了,返回了b需要的属性 a(虽然是半成品 A@2321)

resolveReference 结束,返回 A@2321

resolveValueIfNecessary结束,返回 A@2321

退回到applyPropertyValues

完成属性b设值a( A@2321)

此时populateBean

(回顾:此时就完成了b的setter注入a),完成了b初始化

继续,由于在前面,bean-b进行了提前暴露,执行if语句块,所以我们这次flase(意味不需要提前暴露了)

执行getSingleton, 由于从一级缓存中获取不到,且b在创建中,执行if语句块

从二级缓存中获取bean-b(肯定是没有的),下图中可以看到我们在二级缓存中也获取不到bean-b,并且不需要提前暴露了,所以不需要执行if语句块

所以就直接返回null

返回B@2627

此时我们就结束了对createBean ,并返回B@2627

于是我们就能够从三级缓存中去获取到b了

返回b 的 getSingleton,此时就是 B@2627

放入一级缓存里面

而且,此时我们这个是新创建的bean ,因此 newSingleton = true,执行addSingleton

  1. 放入一级缓存 b - B@2627
  2. 移除三级缓存
  3. 移除二级缓存

getSingleton 结束,获取到了 b实例 B@2627

doGetBean 结束,返回 b实例 B@2627,也就是这一步结束了完成b的初始化

初始化b结束

那么,接下来,继续完成a的初始化。

resolveReference 结束,返回 b实例 B@2627

resolveValueIfNecessary结束,返回 b实例 B@2627

设值a的属性b

populateBean 结束,完成属性赋值


初始化a ,A@2321

前面我们说过我们已经提前暴露了a到三级缓存池里面放到三级缓存池

执行 getSingleton ,由于

一级缓存里面没有a,且a在创建中,所以 执行if语句块

由于我们在二级缓存里面能够找到a,if语句就不执行了,因为已经完成了对a的放入二级缓存池

返回二级缓存池中的对象,A@2321

于是我们就有了早期暴露对象exposedObject,A@2321,doCreateBean结束

createBean 结束

doGetBean结束,返回到getSingleton

由于这是一个新创建的bean,newSingleton = true,执行addSingleton

addSingleton- 放一级缓存

  1. a 放入一级缓存:a - A@2321
  2. 移除二级缓存
  3. 移除三级缓存

getSingleton 结束

doGetBean 结束

触发所有未加载的实例a - 结束

触发所有未加载的实例b - 开始


执行getSingleton

此时,我们的b已经放入了一级缓存了哦,此处就已经完成了b放入一级缓存池,不执行if语句块,返回B@2627

而我们getGetBean的返回对象bean,就是getSingleton 返回的对象B@2627

触发所有未加载的实例b - 结束


preInstantiateSingletons 后面一个循环的语句块,由于这次我们关注的是"循环依赖",就不着重分析这块 就直接过了

finishBeanFactoryInitialization 执行结束

refresh 执行结束

ClassPathXmlApplicationContext 执行结束


断点数量如下:


流程示例图

相关推荐
bro_Java666几秒前
链表进阶2:双向链表的构建
java·数据结构·链表
DianSan_ERP22 分钟前
多平台订单自动下载与回传的技术实现:从消息推送到状态闭环引言
java·linux·服务器·前端·网络·架构·自动化
Joe_Wang524 分钟前
【从0到1学习JVM · 13】点进JDK源码只有一个分号?搞懂本地方法栈与JNI机制
java·jvm·学习·本地方法栈
AI 算法大模型备案~当当43 分钟前
各地备案数量怎么看:一份属地公告的认读与台账方法
java·数据库·人工智能
智鸟科技GemeOpen开发者智能设备1 小时前
GemeOpen 智能音箱 GSSM0B - 播放控制(Java示例)
java·开发语言·智能音箱
今天的砖头有点烫手啊1 小时前
Java IO/NIO/AIO 演进:从 BIO 到 Netty 的底层逻辑
java·jvm·nio
谢亮_vipxieliang2 小时前
Java 21 新特性实战:Record、Sealed、模式匹配
java·开发语言
霸道流氓气质2 小时前
LLM 应用限流与熔断机制完全指南:从多层防护架构到Java生产级弹性实战
java·开发语言·架构
不会就选b2 小时前
算法日常・每日刷题--<动态规划>1
java·数据结构·算法
晚安code2 小时前
JVM 内存结构入门:程序计数器、虚拟机栈、本地方法栈与堆的溢出诊断
java·jvm