《系列二》-- 8、单例bean的创建
阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。
写在开始前的话:
阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了:
- beans
- core
- context
实际上我在博客里贴出来的还只是一部分内容,更多的内容,我放在了个人,fork自 spring 官方源码仓了; 而且对源码的学习,必须是要跟着实际代码层层递进的,不然只是干巴巴的文字味同嚼蜡。
这个仓设置的公共仓,可以直接拉取。
1 源码入口概述
不管是何种作用域的bean 创建,最终都会指向: “如何从零开始创建bean”,而这个话题前文已经讲解过了。
本文重点介绍,单例作用域的bean 在这个过程中的动作有何异同。

这里显然是定义了一个回调操作:
sharedInstance 声明的类型是 Object
Object sharedInstance;
看 sharedInstance 的定义,重点看它的第二个参数,经典匿名内部类写法。这里 createBean() 接收 了bean 相关配置信息;
当在 getSingleton(beanName, ObjectFactory) 方法内部调用 ObjectFactory 的 getObject() 方法时,这些参数将会真正登场。
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
// 准备完成后回调 由子类 AbstractAutowireCapableBeanFactory 实现方法
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
// 出错,单例工厂销毁该 bean
destroySingleton(beanName);
throw ex;
}
}
});
接下来我们先看看 getSingleton(beanName, ObjectFactory) 干了什么:
2 getSingleton(beanName, ObjectFactory) 的行为
口头挨个介绍过于干巴巴了,下边直接贴出加了注释的代码:
/**
* Return the (raw) singleton object registered under the given name,
* creating and registering a new one if none registered yet.
*
* @param beanName the name of the bean
* @param singletonFactory the ObjectFactory to lazily create the singleton
* with, if necessary
* @return the registered singleton object
*/
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 获得单例对象池的锁 [排他性], 其它线程想要获取该对象池的锁,只能等待
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) { // 新创建bean,单例对象池中不存在,否则直接方法返回
if (this.singletonsCurrentlyInDestruction) { // bean 已经被废弃,不能再加载它,抛异常退出
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
// 进行bean重复创建的校验,如果当前bean已经在创建流程中抛异常,众所周知单例bean必须唯一
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 校验通过后,调用 ObjectFactory 的 getObject() 进一步回调 createBean() 方法
singletonObject = singletonFactory.getObject();
// 有没有觉得这也太简单了?其实不然,bean 加载的重头戏还在后边: createBean()
newSingleton = true;
} catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
} catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
} finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// bean 首次加载后再次校验,它是否是真的第一次被加载
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 如果是一个新的单例bean,那么把它假如缓存中,从这里进去看到的一定会是:三级缓存
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
总结
针对单例作用域bean 的创建,这里会根据缓存进行判断,需要保证全局唯一。
getSingleton() 返回 sharedInstance时,实际上最终得到的是回调: createBean()的返回结果
sharedInstance 就只可能有两种类型: 普通bean 或者 FactoryBean
然后进入如下逻辑,根据 sharedInstance 提取实际的bean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
《系列二》-- 8、单例bean的创建的更多相关文章
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
- Spring 源码学习 - 单例bean的实例化过程
本文作者:geek,一个聪明好学的同事 1. 简介 开发中我们常用@Commpont,@Service,@Resource等注解或者配置xml去声明一个类,使其成为spring容器中的bean,以下我 ...
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- spring创建单例bean
(使用的spring版本是3.2.10) 在xml文件中配置一个普通的bean,默认使用单例,创建该bean的调用栈如下: ClassPathXmlApplicationContext //Class ...
- Spring IOC(三)单例 bean 的注册管理
Spring IOC(三)单例 bean 的注册管理 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 在 Spring 中 ...
- Spring单例Bean和线程安全
Spring的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢?例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框 ...
- 小菜学习设计模式(二)—单例(Singleton)模式
前言 设计模式目录: 小菜学习设计模式(一)—模板方法(Template)模式 小菜学习设计模式(二)—单例(Singleton)模式 小菜学习设计模式(三)—工厂方法(Factory Method) ...
- 5.2:缓存中获取单例bean
5.2 缓存中获取单例bean 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了.前面已经提到过,单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例 ...
- 【Spring源码分析】非懒加载的单例Bean初始化过程(上篇)
代码入口 上文[Spring源码分析]Bean加载流程概览,比较详细地分析了Spring上下文加载的代码入口,并且在AbstractApplicationContext的refresh方法中,点出了f ...
- 【Spring源码分析】非懒加载的单例Bean初始化过程(下篇)
doCreateBean方法 上文[Spring源码分析]非懒加载的单例Bean初始化过程(上篇),分析了单例的Bean初始化流程,并跟踪代码进入了主流程,看到了Bean是如何被实例化出来的.先贴一下 ...
随机推荐
- [转帖]是什么让 Redis“气急败坏”回击:13 年来,总有人想替 Redis 换套新架构
https://www.infoq.cn/article/AlF5NIhHdskayl0MTyQG 回击就代表输了?! 今年年中,一位前谷歌.前亚马逊的工程师推出了他创作的开源内存数据缓存系统 Dra ...
- [转帖]Linux AWK工作原理
https://www.cnblogs.com/yeyuzhuanjia/p/13967513.html 本篇文章我们主要为大家介绍 AWK 是如何工作的. AWK 工作流程可分为三个部分:1.读输入 ...
- [转帖]发布即巅峰!万字长文:Java性能调优六大工具:MAT内存分析工具
jianshu.com/p/4ed3dd8b7b83 MAT是MemoryAnalyzerTool的简称,它是一款功能强大的Java堆内存分析器,可以用于查找内存泄漏以及查看内存消耗情况.MAT是 基 ...
- [转帖]陈巍谈芯:NLP里比BERT更优秀的XLNet长什么样?
https://zhuanlan.zhihu.com/p/447836322 目录 收起 一.XLNet的优势 1)独得AR与AE两大绝学 2)集成了Tansformer-XL 二.XLNet的结 ...
- CentOS8 解决 yum元数据下载失败的方法
背景 最近同事反馈CentOS的机器无法再现安装 rpm包了. 提示信息主要是下载源数据失败. 今天上午抽时间进行了一下简单验证, 晚上进行一下总结. 避免遗忘. 认为与Redhat被IBM收购之后, ...
- js 删除数组中的某一个内容
<script type="text/javascript"> let list=["为","啊发大水","阿斯蒂芬& ...
- 从源码中解析fabric区块数据结构(一)
从源码中解析fabric区块数据结构(一) 前言 最近打算基于fabric-sdk-go实现hyperledger fabric浏览器,其中最重要的一步就是解析fabric的上链区块.虽说fabric ...
- IM开源项目OpenIM部署文档-从准备工作到nginx配置
IM开源项目OpenIM部署文档-从准备工作到nginx配置 2022-11-14 22:27·OpenIM 一.准备工作 运行环境 linux系统即可, Ubuntu 7.5.0-3ubuntu1~ ...
- easyui 使用不同的url以获取不同数据源信息
转载 https://www.bbsmax.com/A/kjdw1x06JN/ https://blog.csdn.net/lixinhui199/article/details/50724081 参 ...
- Centos8 配置IP地址与阿里YUM源
Centos8 系统中无法找到network.service网络服务,默认已经被nmcli替换了,所以修改方式略微变化,在/etc/sysconfig/network-scripts/里也看不到任何脚 ...