《系列二》-- 5、单例bean缓存的获取
阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。
写在开始前的话:
阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了:
- beans
- core
- context
实际上我在博客里贴出来的还只是一部分内容,更多的内容,我放在了个人,fork自 spring 官方源码仓了; 而且对源码的学习,必须是要跟着实际代码层层递进的,不然只是干巴巴的文字味同嚼蜡。
这个仓设置的公共仓,可以直接拉取。
回到 doGetBean 初始的位置:
1 判断bean是否完成整个加载流程
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
如下是 singletonObjects 的定义,一个线程安全的HashMap,顾名思义只有是单例的bean被成功加载后才会被它缓存,所以非单例bean 是不可能从中获取到的。
如果我们请求的bean 是单例bean且已经被加载过,完成了整个加载流程,此时程序在方法第一行就已经可以退出了。
不是就接着往下:
2 判断当前bean是否被加载过,是否已作为提前暴露的bean
这里的逻辑其实很简单,无非就是双重锁机制。第一次获取到null后,判断当前bean是否已经进入创建流程 isSingletonCurrentlyInCreation 这个方法名很直观。
isSingletonCurrentlyInCreation 返回true时,说明该bean已经在别的线程 (或递归循环依赖) 中进入创建流程了。
【PS * 前边提到过,bean被加载时,都会递归的去加载它所依赖的 bean】
这里还有另一位主角:
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
它用于提前暴露bean, 你看这里先取了一次,没取到才正式进入创建流程,同样也是为了避免提前暴露动作重复。
关于循环依赖
还有另一位需要被重视的成员:
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
看到了么,它保存的对象实现了 ObjectFactory 接口,不陌生吧,前边讲循环依赖消解的文章介绍过它。
这里还有另一个很有意思的现象不知道你有没有注意到:earlySingletonObjects保存该bean之后,就立即从 singletonFactories 中移除了。
这里说明他们极有可能是一个传递关系:当一个bean 第一次被加载时,默认会保存到 singletonFactories,
这时候这个bean 还在创建流程中,可能出现的情况是这时有另一个 bean 也依赖于它 (参考循环依赖,如果 spring 加载bean的过程是单线程,这时铁定出现循环依赖了。)
根据前边讲循环依赖的文章:
this.earlySingletonObjects.put(beanName, singletonObject);
bean的提前暴露它不就来了么?别看这里只有几行代码,结合上下文之后才会知道它的重要性。
前边讲到 bean 从 singletonFactories 传递到 earlySingletonObjects
那么我们再去追究下, singletonFactories 中是什么时候注入的,然后发现了只有一个地方调用了其上的:singletonFactories.put() 方法
再往上追踪,我们发现了一个名叫: doCreateBean 的方法,这是我们后续流程中关注的方法;它处于从 0 开始创建一个bean 的流程中。
先记住它,后边我们会再见到它的。
结合上边分析的流程,这里还有另一个定义,那就是三级缓存;spring 在消解循环依赖时引入了三级缓存:
一级缓存: Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256)
二级缓存: Map<String, Object> earlySingletonObjects = new HashMap<>(16)
三级缓存: Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16)
首先时创建全新bean 时,将其注入了 三级缓存中。
所以创建bean 的流程时,先从一级缓存获取,如果成功直接返回;
否则从二级缓存中,成功获取到bean,那么直接返回;
如果从二级缓存也无法获取,那么尝试从三级缓存获取,若从三级缓存成功获取,那么从三级缓存中移除该bean,并转移到二级缓存中。
最终,若三级缓存也无法获取,说明是在获取一个从未被加载的 bean。
最终,第一次被加载的bean,最初会被缓存到,三级缓存中,bean 创建流程中,关注上述提到的 doCreateBean()方法即可闭环。
《系列二》-- 5、单例bean缓存的获取的更多相关文章
- 5.2:缓存中获取单例bean
5.2 缓存中获取单例bean 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了.前面已经提到过,单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例 ...
- Spring源码分析(十三)缓存中获取单例bean
摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了 ...
- Spring IOC(三)单例 bean 的注册管理
Spring IOC(三)单例 bean 的注册管理 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 在 Spring 中 ...
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
- 【Spring源码分析】非懒加载的单例Bean初始化过程(上篇)
代码入口 上文[Spring源码分析]Bean加载流程概览,比较详细地分析了Spring上下文加载的代码入口,并且在AbstractApplicationContext的refresh方法中,点出了f ...
- Spring源码分析:非懒加载的单例Bean初始化过程(上)
上文[Spring源码分析]Bean加载流程概览,比较详细地分析了Spring上下文加载的代码入口,并且在AbstractApplicationContext的refresh方法中,点出了finish ...
- Spring单例Bean和线程安全
Spring的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢?例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框 ...
- Spring(六)核心容器 - 注册单例 Bean 实例、SingletonBeanRegistry 简介
前言 上篇文章我们对注册 Bean 的核心类 BeanDefinitionRegistry 进行了讨论,这里的注册 Bean 是指保存 Bean 的相关信息,也就是将 Bean 定义成 BeanDef ...
- Spring 源码学习 - 单例bean的实例化过程
本文作者:geek,一个聪明好学的同事 1. 简介 开发中我们常用@Commpont,@Service,@Resource等注解或者配置xml去声明一个类,使其成为spring容器中的bean,以下我 ...
随机推荐
- [转帖]Linux内存管理(一)——从硬件角度看内存管理
从硬件角度看内存管理 ①.在操作系统还没有出来之前,程序都是被存放在卡片上,计算机读取一张卡片就运行一条指令.这种从外部存储介质上直接运行指令的方法效率很低 ②.单道编程的内存管理 所谓单道,就是整个 ...
- [转帖]Kingbase实现Oracle userenv函数功能
目录 1. 问题 2. 文档概述 3. Oracle userenv()函数功能调研 3.1. 函数名称/函数原型 3.2. 函数功能 3.3. 参数介绍 3.3.1. Parameter 3.4. ...
- [转帖]QPS 最高提升 91% | 腾讯云 TKE 基于 Cilium eBPF 提升 k8s Service 性能
https://my.oschina.net/cncf/blog/5121393 朱瑜坚,腾讯云后台工程师,主要负责腾讯云 TKE 容器网络的构建和相关网络组件的设计.开发和维护工作.张浩,腾讯云 ...
- Linux KVM网络处理过程
Linux KVM网络处理过程 总体解决方法 本次遇到的问题是KVM的网桥处理不小心导致系统无法连接.处理简要总结: 进入机房,给IPMI插上网线, 开机点 Del 进入bios 设置IMPI的地址 ...
- Ant Design Vue栅格Grid的使用
栅格系统的设计理念 建议横向排列的盒子数量最多四个,最少一个. 因此我们的span一般设置为3或者4 小屏幕的话就另当别论了 栅格系统的简单介绍 1.通过row在水平方向建立一组column(简写 c ...
- 当爬虫工程师遇到 CTF丨B 站 1024 安全攻防题解
答案参考 第一题:a1cd5f84-27966146-3776f301-64031bb9 第二题:36c7a7b4-cda04af0-8db0368d-b5166480 第三题:9d3c3014-6c ...
- 西门子PLC高校作业以及创新项目
抢答器 在主持人按下启动按钮,3秒内
- MySQL 之基础命令(精简笔记)
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RD ...
- Python 多线程爬取西刺代理
西刺代理是一个国内IP代理,由于代理倒闭了,所以我就把原来的代码放出来供大家学习吧. 首先找到所有的tr标签,与class="odd"的标签,然后提取出来. 然后再依次找到tr标签 ...
- 《重学Java设计模式》作者开始录视频了!
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 1. 前言 哈哈哈,终于对B站下手了! 大家好,我是小傅哥,在紧张.羞涩到适应后,哈哈哈,终于 ...