• 【Spring】三级缓存


    结合文章:循环依赖

    测试代码如下:

    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");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    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");
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    执行refresh 方法
    image.png
    执行finishBeanFactoryInitialization 方法
    image.png
    执行preInstantiateSingletons 方法
    image.png

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

    image.png

    getBean( doGetBean) - 获取单例bean

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

    getSingleton() - 获取单例bean

    去缓存查看时候有 bean - a
    image.png
    实际上就是通过双重校验锁,去查看一级缓存中是否有 bean-a 并且没有在创建中 ,所以就返回 null
    image.png
    由于返回了 null ,则 继续往下走,去创建bean-a实例
    image.png由于我们 bean-a 是单例的,所以就执行下面的语句块
    image.png
    再进入内部,发现返回的其实就是执行的就是传参的 createBean(beanName, mbd, args)
    image.png
    image.png

    createBean(doCreateBean) - 创建bean

    image.png

    createBeanInstance - 创建并返回bean

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

    addSingletonFactory -放三级缓存

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

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

    image.png

    populateBean - 属性设值

    image.png

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

    image.png

    applyPropertyValues - 设值属性

    image.png
    resolveValueIfNecessary 实际上调用了 resolveReference
    image.png
    resolveReference 也是通过beanFactory 中获取bean
    image.png


    初始化b开始

    而由于此时工厂里面没有bean-b,没有进行初始化
    实际上是调用了doGetBean,又开始了 实例化 bean-b的过程,getBean
    image.png

    从缓存中获取b

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

    创建bean-b实例

    相当于重复了createBean
    由于我们在前一步返回的是null,所以就去执行else语句块的内容
    image.png
    image.png
    常见的是单例bean,进入方法,执行的是createBean方法
    image.png
    image.png
    image.png
    调用了doCreateBean方法
    image.png
    createBeanInstance , B@2627
    image.png

    提前暴露bean-b

    addSingletonFactory
    image.png
    三级缓存中放入 b - lambda@2644
    image.png
    此处我们也可以看到在三级缓存中有两个
    image.png

    b 属性设值

    调用 populateBean
    image.png
    applyPropertyValues
    image.png
    这里我们可以看到 b 是需要a的(符合我们 前面的需求:a b 互相引用)
    image.png
    applyPropertyValues 里面又调用了 resolveValueIfNecessary
    image.png
    resolveValueIfNecessary 又调用了 resolveReference
    image.png
    resolveReference 调用了 beanFactory.getBean,getBean又是通过doGetBean去获取
    image.png
    image.png
    image.png
    先从一级缓存中去获取bean-a ,返回null
    image.png
    由于一级缓存中没有且a在创建中,执行if语句块
    image.png
    查看二级缓存中是否有a,没有,执行if 语句块
    image.png
    再从三级缓存中去获取a, 这里是能够获取从三级缓存中获取到的点击访问
    image.png
    image.png

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

    image.png
    由于能够从三级缓存中去获取到半成品a,A@2321
    image.png
    doGetBean 返回 从三级缓存中获取到的A@2321,所以执行if语句快,发现最后返回的是bean,bean是通过getObjectForBeanInstance 去获取的
    image.png
    getObjectForBeanInstance,又去调用了 super.getObjectForBeanInstance
    image.png
    getObjectForBeanInstance 就是返回了 A@2321
    image.png
    doGetBean 结束了,返回了b需要的属性 a(虽然是半成品 A@2321)
    image.png
    resolveReference 结束,返回 A@2321
    image.png
    resolveValueIfNecessary结束,返回 A@2321
    image.png
    退回到applyPropertyValues
    image.png
    完成属性b设值a( A@2321)
    image.png
    此时populateBean
    (回顾:此时就完成了b的setter注入a),完成了b初始化
    image.png
    继续,由于在前面,bean-b进行了提前暴露,执行if语句块,所以我们这次flase(意味不需要提前暴露了)
    image.png
    执行getSingleton, 由于从一级缓存中获取不到,且b在创建中,执行if语句块
    image.png
    从二级缓存中获取bean-b(肯定是没有的),下图中可以看到我们在二级缓存中也获取不到bean-b,并且不需要提前暴露了,所以不需要执行if语句块
    image.png
    所以就直接返回null
    image.png
    返回B@2627
    image.png
    此时我们就结束了对createBean ,并返回B@2627
    image.png
    于是我们就能够从三级缓存中去获取到b了
    返回b 的 getSingleton,此时就是 B@2627
    image.png

    放入一级缓存里面

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

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

    image.png
    getSingleton 结束,获取到了 b实例 B@2627
    image.png
    doGetBean 结束,返回 b实例 B@2627,也就是这一步结束了完成b的初始化
    image.png

    初始化b结束

    那么,接下来,继续完成a的初始化。
    resolveReference 结束,返回 b实例 B@2627
    image.png
    resolveValueIfNecessary结束,返回 b实例 B@2627
    image.png
    设值a的属性b
    image.png
    populateBean 结束,完成属性赋值
    image.png


    初始化a ,A@2321
    image.png
    前面我们说过我们已经提前暴露了a到三级缓存池里面放到三级缓存池
    image.png
    执行 getSingleton ,由于
    一级缓存里面没有a,且a在创建中,所以 执行if语句块
    image.png
    由于我们在二级缓存里面能够找到a,if语句就不执行了,因为已经完成了对a的放入二级缓存池

    image.png

    返回二级缓存池中的对象,A@2321
    image.png
    于是我们就有了早期暴露对象exposedObject,A@2321,doCreateBean结束
    image.png
    createBean 结束
    image.png
    doGetBean结束,返回到getSingleton
    image.png
    由于这是一个新创建的bean,newSingleton = true,执行addSingleton
    image.png

    addSingleton- 放一级缓存

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

    image.png
    getSingleton 结束
    image.png
    doGetBean 结束
    image.png

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

    image.png

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

    image.png
    image.png
    执行getSingleton
    image.png
    此时,我们的b已经放入了一级缓存了哦,此处就已经完成了b放入一级缓存池,不执行if语句块,返回B@2627
    image.png
    而我们getGetBean的返回对象bean,就是getSingleton 返回的对象B@2627
    image.png

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

    image.png


    preInstantiateSingletons 后面一个循环的语句块,由于这次我们关注的是“循环依赖”,就不着重分析这块 就直接过了
    image.png
    finishBeanFactoryInitialization 执行结束
    image.png
    refresh 执行结束
    image.png
    ClassPathXmlApplicationContext 执行结束
    image.png


    断点数量如下:
    image.png


    流程示例图
    纠错:

    1. 标题为 :初始化a 初始化b
    2. 结尾:a初始化结束 b初始化结束
      在这里插入图片描述
  • 相关阅读:
    SHAP解释模型(二)
    Codeforces Round #803 (Div. 2) VP补题
    javaSE--数据类型(复习)
    FITC标记的大鼠抗小鼠CD11b抗体,FITC Rat Anti-Mouse CD11b
    LeetCode-数组-No48旋转图像
    自己动手实现rpc框架(一) 实现点对点的rpc通信
    wy的leetcode刷题记录_Day45
    bryntum gantt 5.0.6
    快递物流类API推荐
    I2C协议
  • 原文地址:https://blog.csdn.net/qq_43718048/article/details/136151365