阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。

写在开始前的话:

阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了:

  • beans
  • core
  • context

实际上我在博客里贴出来的还只是一部分内容,更多的内容,我放在了个人,fork自 spring 官方源码仓了; 而且对源码的学习,必须是要跟着实际代码层层递进的,不然只是干巴巴的文字味同嚼蜡。

https://gitee.com/bokerr/spring-framework-5.0.x-study

这个仓设置的公共仓,可以直接拉取。

Spring源码阅读系列--全局目录.md

回到 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缓存的获取的更多相关文章

  1. 5.2:缓存中获取单例bean

    5.2  缓存中获取单例bean 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了.前面已经提到过,单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例 ...

  2. Spring源码分析(十三)缓存中获取单例bean

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了 ...

  3. Spring IOC(三)单例 bean 的注册管理

    Spring IOC(三)单例 bean 的注册管理 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 在 Spring 中 ...

  4. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  5. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

  6. 【Spring源码分析】非懒加载的单例Bean初始化过程(上篇)

    代码入口 上文[Spring源码分析]Bean加载流程概览,比较详细地分析了Spring上下文加载的代码入口,并且在AbstractApplicationContext的refresh方法中,点出了f ...

  7. Spring源码分析:非懒加载的单例Bean初始化过程(上)

    上文[Spring源码分析]Bean加载流程概览,比较详细地分析了Spring上下文加载的代码入口,并且在AbstractApplicationContext的refresh方法中,点出了finish ...

  8. Spring单例Bean和线程安全

    Spring的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢?例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框 ...

  9. Spring(六)核心容器 - 注册单例 Bean 实例、SingletonBeanRegistry 简介

    前言 上篇文章我们对注册 Bean 的核心类 BeanDefinitionRegistry 进行了讨论,这里的注册 Bean 是指保存 Bean 的相关信息,也就是将 Bean 定义成 BeanDef ...

  10. Spring 源码学习 - 单例bean的实例化过程

    本文作者:geek,一个聪明好学的同事 1. 简介 开发中我们常用@Commpont,@Service,@Resource等注解或者配置xml去声明一个类,使其成为spring容器中的bean,以下我 ...

随机推荐

  1. [转帖]Linux内存管理(一)——从硬件角度看内存管理

    从硬件角度看内存管理 ①.在操作系统还没有出来之前,程序都是被存放在卡片上,计算机读取一张卡片就运行一条指令.这种从外部存储介质上直接运行指令的方法效率很低 ②.单道编程的内存管理 所谓单道,就是整个 ...

  2. [转帖]Kingbase实现Oracle userenv函数功能

    目录 1. 问题 2. 文档概述 3. Oracle userenv()函数功能调研 3.1. 函数名称/函数原型 3.2. 函数功能 3.3. 参数介绍 3.3.1. Parameter 3.4. ...

  3. [转帖]QPS 最高提升 91% | 腾讯云 TKE 基于 Cilium eBPF 提升 k8s Service 性能

    https://my.oschina.net/cncf/blog/5121393   朱瑜坚,腾讯云后台工程师,主要负责腾讯云 TKE 容器网络的构建和相关网络组件的设计.开发和维护工作.张浩,腾讯云 ...

  4. Linux KVM网络处理过程

    Linux KVM网络处理过程 总体解决方法 本次遇到的问题是KVM的网桥处理不小心导致系统无法连接.处理简要总结: 进入机房,给IPMI插上网线, 开机点 Del 进入bios 设置IMPI的地址 ...

  5. Ant Design Vue栅格Grid的使用

    栅格系统的设计理念 建议横向排列的盒子数量最多四个,最少一个. 因此我们的span一般设置为3或者4 小屏幕的话就另当别论了 栅格系统的简单介绍 1.通过row在水平方向建立一组column(简写 c ...

  6. 当爬虫工程师遇到 CTF丨B 站 1024 安全攻防题解

    答案参考 第一题:a1cd5f84-27966146-3776f301-64031bb9 第二题:36c7a7b4-cda04af0-8db0368d-b5166480 第三题:9d3c3014-6c ...

  7. 西门子PLC高校作业以及创新项目

    抢答器 在主持人按下启动按钮,3秒内

  8. MySQL 之基础命令(精简笔记)

    MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RD ...

  9. Python 多线程爬取西刺代理

    西刺代理是一个国内IP代理,由于代理倒闭了,所以我就把原来的代码放出来供大家学习吧. 首先找到所有的tr标签,与class="odd"的标签,然后提取出来. 然后再依次找到tr标签 ...

  10. 《重学Java设计模式》作者开始录视频了!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 1. 前言 哈哈哈,终于对B站下手了! 大家好,我是小傅哥,在紧张.羞涩到适应后,哈哈哈,终于 ...