【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 执行结束


断点数量如下:


流程示例图

相关推荐
编程大师哥3 分钟前
vxe-table 透视表分组汇总及排序基础配置
java
8***848216 分钟前
spring security 超详细使用教程(接入springboot、前后端分离)
java·spring boot·spring
9***J62819 分钟前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
M***Z21030 分钟前
SQL 建表语句详解
java·数据库·sql
v***79430 分钟前
Spring Boot 热部署
java·spring boot·后端
执笔论英雄31 分钟前
【RL】python协程
java·网络·人工智能·python·设计模式
galaxyffang42 分钟前
认证、会话管理、授权的区别
java
未名编程1 小时前
Windows 下如何部署 Nacos 并导入配置文件
java·windows
boonya1 小时前
Java中Plugin设计模式的规范应用
java·spring·设计模式·插件模式
杰克尼1 小时前
3. 分巧克力
java·数据结构·算法