《系列二》-- 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,以下我 ...
随机推荐
- 使用 Taro 开发鸿蒙原生应用 —— 探秘适配鸿蒙 ArkTS 的工作原理
背景 在上一篇文章中,我们已经了解到华为即将发布的鸿蒙操作系统纯血版本--鸿蒙 Next,以及各个互联网厂商开展鸿蒙应用开发的消息.其中,Taro作为一个重要的前端开发框架,也积极适配鸿蒙的新一代语言 ...
- vue中diff算法处理新旧节点的流程
vue中diff算法处理新旧节点的流程 patch函数的作用 function patch(oldVnode: VNode | Element, vnode: VNode): VNode { let ...
- echarts去除坐标轴上的x和y轴
通过 show:false控制手否显示 <!DOCTYPE html> <html lang="en"> <head> <meta cha ...
- typeof的用法和注意点
基本数据类型和查看数据类型 1==>js有六种基本数据类型. String Boolean Number null underfined Symbol [6种] 但是<你不知道的javas ...
- STM32CubeMX教程27 SDIO - 读写SD卡
1.准备材料 正点原子stm32f407探索者开发板V2.4 STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK-Arm) ST-LINK/V2驱动 ...
- TienChin 渠道管理-渠道导出
ChannelController /** * 导出渠道列表 */ @PreAuthorize("hasPermission('tienchin:channel:export')" ...
- x64dbg 实现插件Socket反向通信
编写一个带有socket通信功能的插件,x64dbg运行后,用户点击链接按钮可直接连接到外部的python中,python作为服务端,当x64dbg内部出现某个事件后,自动将消息推送到外部python ...
- Python 代码推送百度链接
通过代码实现抓取个人博客中某一页指定文章链接,并批量将该链接推送到百度站长平台,起到快速收录的目的. import sys import requests from bs4 import Beauti ...
- C++文件处理工具类
C++文件处理工具类,hpp文件,使用时只需要include即可,跨平台 File.hpp #ifndef FILE_HPP #define FILE_HPP #include <fstream ...
- 疾速7600MT/s!KELVV科赋CRAS V RGB DDR5内存图赏
12月18日消息,KLEVV科赋日前推出新款大容量DDR5内存套装,满足游戏玩家.内容创作者和高端PC爱好者的需求. 现在,KLEVV科赋CRAS V RGB内存套装已经来到我们评测室,下面为大家带来 ...