Spring框架宝典:彻底理解三级缓存策略

一、循环依赖概念

在Spring应用中,循环依赖指的是两个或多个Bean之间相互引用,造成了一个环状的依赖关系。举例来说,如果Bean A依赖于Bean B,同时Bean B也依赖于Bean A,就形成了循环依赖。这种情况下,Spring容器在创建这些Bean时会陷入无限循环,导致应用启动失败或者出现其他不可预测的问题。

二、解决方案(三级缓存)

Spring容器在解决循环依赖问题时使用了三级缓存的机制。在创建Bean的过程中,Spring容器会利用三个缓存来处理实例化和依赖注入,确保即使在存在循环依赖的情况下也能正确创建Bean。

1、Spring三级缓存流程

让我们深入了解一下这个过程:

  1. 当 Spring IOC 容器扫描到一个 Bean 时,它会先将其实例化并放入一级缓存中。同时,会为这个 Bean 创建一个 ObjectFactory 对象,用于后续的依赖注入。
  2. 如果 Bean 依赖其他 Bean,Spring 在创建 Bean 实例时,会先检查一级缓存。如果 Bean 还不存在于一级缓存中,Spring 开始实例化该 Bean,并将其添加到三级缓存中。这时,Bean 具有自己的内存地址。
  3. 接下来,Spring 填充 Bean 的属性。如果属性依赖于其他 Bean,Spring 会从一级缓存中获取对应的 Bean。如果 Bean 不存在于一级缓存中,Spring 会从三级缓存中获取 ObjectFactory,执行工厂方法,创建 Bean 的早期引用,并将其放入二级缓存中。
  4. 在回溯过程中,当 Bean 成为“成品”时,它会从三级缓存中移除,并放入一级缓存中。这样,Bean 的初始化就完成了。
  5. 最终,所有 Bean 都进入一级缓存,准备供用户使用。

2、源码分解

Spring框架的IoC容器是Java开发者广泛使用的组件之一,它通过控制反转的方式管理Bean的生命周期。Spring在创建Bean的过程中,为了避免不必要的性能开销,引入了多级缓存机制。本文将从源码的角度分析Spring是如何创建Bean的,重点探讨Spring中缓存的使用。

Step1: Spring容器的初始化

Spring容器的初始化始于AbstractApplicationContext的refresh()方法的调用,该方法触发了容器的刷新操作。 AbstractApplicationContext是Spring加载上下文的入口。

org.springframework.context.support.AbstractApplicationContext#refresh()

这里最关键的步骤是obtainFreshBeanFactory(),它会创建一个新的DefaultListableBeanFactory,这个Factory在后续会完成具体的bean加载和创建工作。

Step2: Bean的定义和注册

在前面的obtainFreshBeanFactory()方法中,Spring会加载所有的BeanDefinition,这些BeanDefinition可能来自XML文件、注解扫描等。而加载BeanDefinition的过程,就是调用DefaultListableBeanFactory的registerBeanDefinition方法。

org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

registerBeanDefinition方法就是将加载的BeanDefinition存储到一个内部Map中,留待后续使用。

Step3: Bean的获取与创建

Step3-1:Bean的获取getBean()

当我们需要使用某个bean时,就会调用AbstractBeanFactory的getBean(),它是获取bean的入口。

org.springframework.beans.factory.support. AbstractBeanFactory#getBean()

Spring在获取一个bean实例时,首先会检查这个bean是否已存在于单例缓存容器singletonObjects中。

如果缓存存在,直接返回缓存的bean实例,避免重复创建。如果缓存不存在,则调用createBean方法创建一个新的bean实例。

@Override
public Object getBean(String name) throws BeansException {
    // 检查单例缓存
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null) {
        // 缓存存在,直接返回
        return sharedInstance;
    }
    // 缓存不存在,则创建bean
    return createBean(beanName, ...);
}
Step3-2:获取缓存中的Bean(getSingleton)

Spring会将创建的单例对象存入singletonObjects单例缓存中,确保下次获取该bean时可以直接从缓存中获取。

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton

如果单例缓存中不存在,就会尝试从单例缓存的子逻辑缓存中获取:

// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 检查是否在创建中
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 尝试从earlySingletonObjects中获取,earlySingletonObjects属于二级缓存
        synchronized (this.singletonObjects) {
             singletonObject = this.earlySingletonObjects.get(beanName);
             if (singletonObject == null && allowEarlyReference) {
                 // 从三级缓存singletonFactories中获取
                 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                 if (singletonFactory != null) {
                     singletonObject = singletonFactory.getObject();
                     // 将三级缓存获取到的bean放入earlySingletonObjects二级缓存
                     this.earlySingletonObjects.put(beanName, singletonObject);
                 }
             }
        }
    }
    return singletonObject;
}

这里可以看到,Spring维护了三级缓存:

  1. singletonObjects: 单例bean缓存
  2. earlySingletonObjects: 早期曝光的单例bean缓存
  3. singletonFactories: 单例bean工厂缓存

当bean不存在于singletonObjects一级缓存时,Spring会先检查该bean是否正在创建中,如果是则尝试从earlySingletonObjects二级缓存获取。如果二级缓存也没有,那么就从singletonFactories三级缓存中获取。这样设计三级缓存主要是为了解决循环依赖的问题。

Step3-3:Bean的创建createBean

在AbstractAutowireCapableBeanFactory的createBean方法中,会先尝试从缓存中获取该bean,如果缓存不存在,才会正式调用createBeanInstance方法实例化该bean。

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean

createBean方法是创建bean的核心逻辑,包括了bean的实例化、属性注入、循环依赖处理、初始化方法回调等。

需要重点关注doCreateBean和populateBean两个方法。

doCreateBean方法的主要工作是实例化bean对象。Spring通过不同的InstantiationStrategy策略来实例化不同的bean,如反射创建、通过工厂方法创建、CGLIB创建等。

populateBean方法会完成属性注入的工作。包括依赖注入、自动装配和循环依赖的处理。循环依赖是Spring处理较为复杂的一个环节,通过三级缓存来完成。

Step3-4:创建的Bean存入缓存

进入doCreateBean方法真正地创建bean实例。

在AbstractAutowireCapableBeanFactory中,创建单例bean后,是通过调用addSingletonFactory方法将bean添加到三级缓存中。

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory

至此spring bean创建与缓存的过程就差不多完成了。

3、三级缓存的必要性

Spring三级缓存解决循环依赖的一个重要原因是确保带有AOP及其它增强的Bean在返回给其他Bean之前必须完全初始化。因为AOP增强通常涉及到代理的创建,这需要在Bean的所有依赖都解决之后进行。如果提前返回一个未完成增强的Bean实例,那么它可能不会按预期工作,因为它缺少了如事务管理、安全检查等关键行为。

三级缓存中的singletonFactories存储的工厂对象允许Spring在Bean完全初始化并应用了所有AOP及其它增强之后,再返回Bean的实例。这样,即使在循环依赖的情况下,也能确保每个Bean都是完整且正确增强的,从而保持了应用的一致性和稳定性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据库介绍(Mysql安装)

前言 工程师再在存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 一、什么是数据库&#xff1f; 文件保存数据有以下几个缺点&#xff1a; 文件的安全性问题文件不利于数据查询和管理文件不利于存储海量数据文件在程序中控制不方便 数据库存储介质&#xff1a; 磁…

【Canvas与艺术】绘制朝鲜国旗

【声明】 该国旗的定位和大小是本人与网上照片比对后估算的&#xff0c;不是精确值。 【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <hea…

四信智能化感知与控制方案,助推灌区续建配套与现代化改造建设

“十四五”明确提到推进大中型灌区节水改造和精细化管理&#xff0c;建设节水灌溉骨干工程&#xff0c;同步推进水价综合改革。 灌区是保障国家粮食安全的重要基础性设施&#xff0c;是实施乡村振兴战略的水利支撑。灌区续建配套与现代化改造是实施乡村振兴战略一项重要任务。为…

一套JAVA语言开发的:危大工程智慧一体化工地系统源码,(后台管理端+APP+可视化大屏端)

智慧工地是指利用移动互联、物联网、智能算法、地理信息系统、大数据挖掘分析等信息技术&#xff0c;提高项目现场的“人•机•料•法•环•安”等施工要素信息化管理水平&#xff0c;实现工程施工可视化智能管理&#xff0c;并逐步实现绿色生态建造。 相关技术&#xff1a;微…

“百团大战”下,20年代的国产数据库如何乘风破浪?

引言 在数字化浪潮的推动下&#xff0c;数据库技术已成为支撑数字经济的坚实基石。腾讯云 TVP《技术指针》联合《明说三人行》特别策划的直播系列——【中国数据库前世今生】&#xff0c;我们将通过五期直播&#xff0c;带您穿越五个十年&#xff0c;深入探讨每个时代的数据库演…

vue3.0(四) ref全家桶以及响应的 源码分析

文章目录 1 ref1.1 ref() 是什么1.2 ref() 特点1.3 创建响应式数据1.4 引用DOM元素1.5 深层响应性1.6 DOM 更新时机1.7 ref源码 2 isRef2.1 isRef运用2.2 isRef源码 3 unref3.1 unref运用3.2 unref源码 4 shallowRef4.1 shallowRef运用4.2 shallowRef源码 5 triggerRef5.1 trig…

SpringCloud系列(10)--Eureka集群原理及搭建

前言&#xff1a;当注册中心只有一个&#xff0c;而且当这个注册中心宕机了&#xff0c;就会导致整个服务环境不可用&#xff0c;所以我们需要搭建Eureka注册中心集群来实现负载均衡故障容错 Eureka架构原理图 1、Eureka集群原理 2、创建Eureka Server端服务注册中心模块 (1)在…

ios微信小程序禁用下拉上拉

第一步&#xff1a; page.json配置页面的"navigationStyle":"custom"属性&#xff0c;禁止页面滑动 "navigationStyle":"custom" 第二步&#xff1a; 页面里面使用scroll-view包裹内容&#xff0c;内容可以内部滑动 <view class&…

LLaMA 3:大模型之战的新序幕

作者 | 符尧 OneFlow编译 翻译&#xff5c;杨婷、宛子琳、张雪聃 本文要点概览&#xff1a; 文本数据的扩展可能已经达到了极限&#xff0c;因为易于获取的网络文本资源&#xff08;如Common Crawl、GitHub、ArXiv等&#xff09;已基本被充分利用。 尽管如此&#xff0c;通过更…

Redis底层数据结构之ZSkipList

目录 一、概述二、ZSkipList结构三、和平衡树和哈希表的对比 redis底层数据结构已完结&#x1f44f;&#x1f44f;&#x1f44f;&#xff1a; ☑️redis底层数据结构之SDS☑️redis底层数据结构之ziplist☑️redis底层数据结构之quicklist☑️redis底层数据结构之Dict☑️redis…

[Diffusion Model 笔记]Score based

目录 概述方法怎么估计score&#xff08;估计噪声就是估计score&#xff09;怎么采样&#xff08;给原始数据加噪声&#xff0c;早期大后来变小&#xff09;inpainting &#xff08;来自补充材料&#xff09;还没有细究的地方&#xff1a; 概述 本文是观看以下视频的笔记&…

使用JMeter模拟设备通过MQTT发送数据

需求&#xff1a; 需要一个工具能够支持MQTT协议发送各种不同的数据。 目的&#xff1a; 模拟小型温室设备反馈&#xff0c;搭建一个测试环境&#xff0c;根据测试的数据显示硬件的状态和数值。 工具&#xff1a;JMeter 环境&#xff1a;需要配置Java运行环境。 操作步骤&a…

机器人操作系统ROS2学习—编译工作空间colcon build报错问题

在ROS2中&#xff0c;工作空间创建完成后&#xff0c;会经常需要编译工作空间。在工作空间dev_ws 下打开一个终端&#xff0c;通过指令Colcon build来编译工作空间。 1、这个过程有可能会出现如下错误: "colconbuild:Duplicate package names not supported" 根据…

阅读笔记——《BLEEM: Packet Sequence Oriented Fuzzing for Protocol Implementations》

【参考文献】Zhengxiong Luo, Junze Yu, Feilong Zuo, Jianzhong Liu, Yu Jiang, Ting Chen, Abhik Roychoudhury, and Jiaguang Sun. Bleem: Packet sequence oriented fuzzing for protocol implementations. In 32nd USENIX Security Symposium (USENIX Security 23), pages…

西湖大学赵世钰老师【强化学习的数学原理】学习笔记2节

强化学习的数学原理是由西湖大学赵世钰老师带来的关于RL理论方面的详细课程&#xff0c;本课程深入浅出地介绍了RL的基础原理&#xff0c;前置技能只需要基础的编程能力、概率论以及一部分的高等数学&#xff0c;你听完之后会在大脑里面清晰的勾勒出RL公式推导链条中的每一个部…

github two-factor authentication是个啥?

最近在逛github时&#xff0c;总是时不时会弹出一下界面&#xff0c;很烦 看到红框里的文字&#xff0c;这明显是强制要求做这个认证&#xff0c;如果不认证4天后账号将不可访问&#xff0c;所以今天花点时间看看怎么做这个认证&#xff0c;点“Enable 2FA now”进入这个界面&a…

Vue后台系统demo小计

创建项目 1.报错 Error: command failed: npm install --loglevel error --legacy-peer-deps 措施1&#xff1a;node.js文件夹属性 》高级 》选择第一个允许 Users(XXX\Users) &#xff08;对我无用&#xff09; 措施2&#xff1a;PowerShell(以管理员身份运行) 》 cd 想存…

java基础之java容器-Collection,Map

java容器 java容器分类一. Collection1. List①. ArrayList② . LinkedList③ . Vector 2. Queue队列①. LinkedList②. PriorityQueue 3. Set集合①. HashSet②. TreeSet 二. Map1. HashMap2.TreeMap3. Hashtable java容器分类 java容器分为两大类&#xff0c;分别是Collecti…

VMWare下建的CentOS7 扩容

记录一下扩容过程中踩过的坑 背景&#xff1a;一年半以前私有化部署了一个gitLab服务&#xff0c;当时只分配了30G的磁盘容量&#xff0c;这两天小伙伴总是反馈gitLab登不上。排查发现是因为磁盘满了 然后就开始了磁盘扩容之旅 各种 vgs\pv\pvdisplay\lv\lvm 等等都没用 一下…

ChatGPT记忆功能终于上线了, OpenAI 官方:用得越久越聪明!

原文 ChatGPT记忆功能终于上线了, OpenAI 官方&#xff1a;用得越久越聪明&#xff01; Aitrainee | 公众号&#xff1a;AI进修生 &#x1f31f; 记得今年2月份OpenAI发布过ChatGPT上线记忆功能的消息&#xff0c;我记得当时还弹出过这个窗口给我&#xff0c;但是仅仅体验了几…