Spring循环依赖-实践三级缓存的再次理解

news2024/12/26 11:56:28

目录

    • Spring循环依赖流程图
    • 场景:**A 依赖B**; **B依赖A、C**; **C依赖A**
      • A,B, C三个类的定义
      • 容器类
      • 测试输入如下
    • 总结

Spring循环依赖流程图

很早之前阅读源码写过总结:
https://github.com/doctording/spring-framework-5.1.3.RELEASE/blob/dev/docs/doc/bean/dependency.md

A,B相互依赖的创建流程即如下图所示:
在这里插入图片描述

场景:A 依赖B; B依赖A、C; C依赖A

A --> B
B --> A, C
C --> A

A,B, C三个类的定义

A

package com4;

public class A {
    private B b;

    public B getB() {
        return b;
    }

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

B

package com4;

public class B {
    private A a;

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }

    private C c;

    public C getC() {
        return c;
    }

    public void setC(C c) {
        this.c = c;
    }
}

C

package com4;

public class C {
    private A a;

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }
}

容器类

仿照spring框架源码编写如下java代码

package com4;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


public class Container {

    Map<String, Class> beanMap = new HashMap<>(16);


    public void addBean(String name, Class clazz){
        beanMap.put(name, clazz);
    }


    Map<String, Object> singletonObjects = new HashMap<>(16);


    Map<String, Object> earlySingletonObjects = new HashMap<>(16);


    Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);


    private void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
        }
    }

    private Object getSingleton(String beanName) throws Exception{
        // 一级别缓存找
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            synchronized (this.singletonObjects) {
                // 二级别缓存找
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null) {
                    // 三级别缓存找
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // lambda执行后 塞到二级缓存中,并清空三级缓存
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                        System.out.println("from singletonFactory to earlySingletonObjects bean:" + beanName);
                    }
                } else { // 多加一个else 理解 earlySingletonObjects 和 singletonFactories
                    System.out.println("just get from earlySingletonObjects:" + beanName);
                }
            }
        }
        return singletonObject;
    }

    private Object getEarlyBeanReference(String name, Object o){
        // 执行lambda,应用后处理 // TODO
        System.out.println("execute lambda to get:" + name);
        return o;
    }

    // 真正创建bean
    public Object doCreateBean(String name) throws Exception{
        System.out.println("crate bean: " + name);
        if (this.singletonObjects.containsKey(name)) {
            return this.singletonObjects.get(name);
        }
        // 初始化实例
        Class clazz = beanMap.get(name);
        Constructor constructor = clazz.getConstructors()[0];
        constructor.setAccessible(true);
        Object o = constructor.newInstance();

        // 加入三级缓存中
        System.out.println("add to singletonFactories bean:" + name);
        singletonFactories.put(name, () -> getEarlyBeanReference(name, o));

        Object exposedObj = o;
        // 设置属性, 有循环依赖,假设field的set方法都是依赖哈
        Field[] fields =  exposedObj.getClass().getDeclaredFields();
        for (Field field : fields) {
            String name2 = field.getName();
            String beanName2 = name2.toUpperCase();
            Class clazz2 = field.getType();
            System.out.println("resolve dependency bean: " + beanName2);
            Object o2 = getSingleton(beanName2);
            if (o2 == null) {
                o2 = doCreateBean(beanName2);
            }
            String beanSetMethodName = "set" + beanName2;
            Method method = clazz.getDeclaredMethod(beanSetMethodName, clazz2);
            method.invoke(exposedObj, o2);
            System.out.println(String.format("bean:%s set dependency bean:%s success", name, name2));
        }

        // 加入以及缓存并清空二级,三级缓存
        System.out.println("addSingleton bean: " + name);
        addSingleton(name, exposedObj);

        return exposedObj;
    }

    public static void main(String[] args) throws Exception {
        Container container = new Container();
        container.addBean("A", A.class);
        container.addBean("B", B.class);
        container.addBean("C", C.class);
        A a = (A) container.doCreateBean("A");
        B b = (B) container.doCreateBean("B");
        C c = (C) container.doCreateBean("C");

        System.out.println("------------------------------");
        System.out.println(a + ":  " + a.getB());
        System.out.println(b + ":  " + b.getA() + ": " + b.getC());
        System.out.println(c + ":  " + c.getA());
    }

测试输入如下

crate bean: A // 创建A
add to singletonFactories bean:A // 加入到三级缓存
resolve dependency bean: B
crate bean: B // 发现依赖B, 创建B
add to singletonFactories bean:B
resolve dependency bean: A // B依赖A
execute lambda to get:A // 三级缓存有A,执行并塞到二级缓存
from singletonFactory to earlySingletonObjects bean:A
bean:B set dependency bean:a success
resolve dependency bean: C
crate bean: C // B 依赖 C, 创建C
add to singletonFactories bean:C
resolve dependency bean: A 
just get from earlySingletonObjects:A // C 依赖 A, 直接从二级缓存取
bean:C set dependency bean:a success
addSingleton bean: C // C创建完成
bean:B set dependency bean:c success
addSingleton bean: B // B创建完成
bean:A set dependency bean:b success
addSingleton bean: A // A创建完成
crate bean: B // 获取B从一级缓存直接取
crate bean: C // 获取C从一级缓存直接取
------------------------------
com4.A@3d075dc0:  com4.B@214c265e
com4.B@214c265e:  com4.A@3d075dc0: com4.C@448139f0
com4.C@448139f0:  com4.A@3d075dc0

可以看到A,B,C 都成功创建, 且依赖关系也正确

总结

关于理解为什么需要三级缓存以及三级缓存的作用,通过本文实际例子是可以充分说明的。

实际上看:只有A,B的相互依赖,二级缓存就够了;只需要存储一个中间对象:A 创建依赖B, B创建依赖A,从这个中间缓存取就能完成创建B, 那么A也创建完成了,这样之后A,B都加入到一级缓存singletonObjects中,很完美。

但是考虑到上述A,B, C就可以多一个缓存了,考虑输出的注释

just get from earlySingletonObjects:A // C 依赖 A, 直接从二级缓存取

B、C都依赖A, B从中间缓存得到lambda且执行后可以再次进行缓存,这样C直接从这个缓存取出来用;依此类推,如果D、E、F…都依赖A,也是直接取出来,这样省了lambda的执行,所以二级缓存减少了这个开销,而三级缓存lambda则是必备,用来延迟加载获取依赖bean,拥有此缓存才能完成相互依赖的设置。

lambda延迟加载可以简单阅读我的博文:Java Function & Supplier 的实际例子对比感受抽象和懒加载


最后请读者仔细阅读代码直接拷贝然后去运行、多看看、已加深理解。如果有不一样的想法欢迎评论此博文进行交流。

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

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

相关文章

8/12 题解

解题思路 贪心&#xff0c;小的搭配大的&#xff0c;和会最小 AC代码 #include <iostream> using namespace std;int main() {int n;cin >> n;int l 1;int r n;while(l < r){ cout << l << ;l;cout << r << ;--r;if(l r){cout …

java 9的新特性解读(3)

目录 语法改进&#xff1a;try语句 String存储结构变更 Motivation Description 那StringBuffer 和 StringBuilder 是否仍无动于衷呢&#xff1f; 集合工厂方法&#xff1a;快速创建只读集合 语法改进&#xff1a;try语句 Java 8 中&#xff0c;可以实现资源的自动…

JavaScript【瀑布流-页面布局、动态设置内容居中、动态设置图片位置、页面触底、上拉加载、页面布局、动态切换、页面布局】(十五)

目录 DOM实操-瀑布流-页面布局 瀑布流特点 DOM实操-瀑布流-动态设置内容居中 DOM实操-瀑布流-动态设置图片位置 DOM实操-瀑布流-页面触底 DOM实操-瀑布流-上拉加载 DOM实操-轮播图-页面布局 轮播图 轮播图特点 DOM实操-轮播图-动态切换 DOM实操-放大镜-页面布局 放大…

Leetcode-每日一题【剑指 Offer 25. 合并两个排序的链表】

题目 输入两个递增排序的链表&#xff0c;合并这两个链表并使新链表中的节点仍然是递增排序的。 示例1&#xff1a; 输入&#xff1a;1->2->4, 1->3->4输出&#xff1a;1->1->2->3->4->4 限制&#xff1a; 0 < 链表长度 < 1000 解题思路 1…

Java对象内存结构、对象在内存是什么样的

我们知道Java对象分配在堆内存中&#xff0c;一个对象在堆内存中的存储布局可以分为三部分&#xff1a; 对象头Header实例数据对齐填充 1. 对象头Header 对象头部分又包含两部分&#xff1a; 第一部分是用于存储对象自身运行时数据&#xff0c;例如哈希码、GC分代年龄等第二…

时序预测 | MATLAB基于扩散因子搜索的GRNN广义回归神经网络时间序列预测(多指标,多图)

时序预测 | MATLAB基于扩散因子搜索的GRNN广义回归神经网络时间序列预测(多指标,多图) 目录 时序预测 | MATLAB基于扩散因子搜索的GRNN广义回归神经网络时间序列预测(多指标,多图)效果一览基本介绍程序设计学习小结参考资料效果一览

Spring5 AOP 默认使用 JDK

这是博主在使用dubbo实现远程过程调用的时候遇到的问题&#xff1a; 我们如果在服务提供者类上加入Transactional事务控制注解后&#xff0c;服务就发布不成功了。原因是事务控制的底层原理是为服务提供者类创建代理对象&#xff0c;而默认情况下Spring是基于JDK动态代理方式创…

LeetCode面向运气之Javascript—第121题-买卖股票的最佳时机-97.77%

LeetCode第121题-买卖股票的最佳时机 题目要求 给定一个数组prices &#xff0c;它的第i个元素prices[i]表示一支给定股票第i天的价格。 你只能选择某一天买入这只股票&#xff0c;并选择在未来的某一个不同的日子卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回…

Martin_DHCP_V3.0 (DHCP自动化泛洪攻击GUI)

Github>https://github.com/MartinxMax/Martin_DHCP_V3.0 首页 Martin_DHCP_V3.0 自动化DHCP洪泛攻击 Martin_DHCP_V3.0 使用方法 安装三方库 #python3 1.RunMe_Install_Packet.py 攻击路由器 #python3 Martin_DHCP_Attack.py 填写网卡 填写攻击次数 开始运行

语音芯片的型号有哪些?为什么强烈推荐使用flash型可擦写的

一、语音芯片的简介 语音芯片的型号有哪些&#xff1f;为什么强烈推荐使用flash型可擦写的芯片。这里我们简单描述一下如下常见类容&#xff1a; 1、他们都有什么特点&#xff1f;以及发展的历程简介 2、常见的语音芯片有哪些&#xff1f; 3、为什么推荐使用flash型可以重复…

Scractch3.0_Arduino_ESP32_学习随记_显示网络天气(二)

这里写目录标题 目的器材程序联系我们 目的 通过C02获取网络天气。并在屏上显示 器材 硬件: 齐护机器人C02 购买地址 软件: scratch3.0 下载地址:官网下载 程序 使用的是公开免费的API&#xff0c;对请求间隔和次数有限制&#xff0c;如果连续获取可能会被封IP&#xff…

机器学习讲解!(多种算法示例 全网最详细!)

机器学习 机器学习是人工智能的一个分支&#xff0c;它研究计算机如何通过自身的学习和经验来提高其性能&#xff0c;而不需要明确的被编程。机器学习算法可以从大量的数据中学习&#xff0c;并能根据这些数据做出预测或分类。机器学习目前已经被广泛应用于许多领域&#xff0…

轻量级 Spring Task 任务调度可视化管理

Spring Task/Spring Scheduler 傻傻分不清 首先做一下“名词解释”&#xff0c;分清楚这两者的区别&#xff1a; Spring Task Spring Task 是 Spring 框架自带的一个任务调度模块&#xff0c;提供了基本的任务调度功能。它是通过 Java 的 Timer 和 TimerTask 类来实现的&…

Qt扫盲-QWidget理论使用总结

QWidget理论使用总结 一、概述二、顶层 控件 和子 控件三、复合控件四、自定义控件和绘制五、大小提示和大小策略六、事件七、一组函数和属性八、QWidget样式表九、透明度和双缓冲十、创建半透明窗口 一、概述 widget 是用户界面的最小单位&#xff1a;它从window系统接收鼠标…

STM32F429IGT6使用CubeMX配置串口通信

1、硬件电路 2、设置RCC&#xff0c;选择高速外部时钟HSE,时钟设置为180MHz 3、配置USART1引脚 4、生成工程配置 5、部分代码 //重定向printf函数 int fputc(int ch, FILE *f) {HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);return ch; } /* USER CODE BE…

初学vue3时应该注意的几个问题

初学vue3时应该注意的几个问题 声明响应式 响应式数据的声明在vue2的时候很简单&#xff0c;在data中声明就行了。但现在可以使用多个方式。 reactive用于声明Object, Array, Map, Set; ref用于声明String, Number, Boolean 使用reactive来声明基础数据类型&#xff08;Str…

华为OD机试真题 Java 实现【寻找相同子串】【2023 B卷 100分】,附详细解题思路

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

Leetcode-每日一题【剑指 Offer 26. 树的子结构】

题目 输入两棵二叉树A和B&#xff0c;判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构&#xff0c; 即 A中有出现和B相同的结构和节点值。 例如: 给定的树 A: 3 / \ 4 5 / \ 1 2 给定的树 B&#xff1a; 4 / 1 返回 true&#xff0…

超全的数据可视化大屏设计组件库 sketch格式

随着大屏可视化设计需求的发展&#xff0c;可视化sketch矢量素材变得越来越受欢迎&#xff0c;它可以为设计师提供丰富的设计元素&#xff0c;帮助他们更高效更快速的完成设计任务。 大屏可视化sketch数量素材是B端可视化设计师们最佳设计资源&#xff0c;它可以帮助设计师轻松…

iOS开发-实现二维码扫一扫Scan及识别图片中二维码功能

iOS开发-实现二维码扫一扫Scan及识别图片中二维码功能 在iOS开发中&#xff0c;会遇到扫一扫功能&#xff0c;扫一扫是使用摄像头扫码二维码或者条形码&#xff0c;获取对应二维码或条形码内容字符串。通过获得的字符串进行跳转或者打开某个页面开启下一步的业务逻辑。 https…