Spring(六)核心容器 - 注册单例 Bean 实例、SingletonBeanRegistry 简介
前言
上篇文章我们对注册 Bean 的核心类 BeanDefinitionRegistry 进行了讨论,这里的注册 Bean 是指保存 Bean 的相关信息,也就是将 Bean 定义成 BeanDefinition,然后放入容器中。除此之外,Spring 还提供一个统一操作单例 Bean 实例的类 SingletonBeanRegistry,通过该类可直接对单例 Bean 的实例进行存储、注册等操作。
SingletonBeanRegistry
SingletonBeanRegistry 是一个接口,其定义了操作单例 Bean 实例的一些基础方法:
public interface SingletonBeanRegistry {
// 注册单例 Bean。其实就是将该 Bean 保存到一个专门存储单例 Bean 实例的Map中,Key是 beanName,Value是对应的单例 Bean 实例
void registerSingleton(String beanName, Object singletonObject);
// 通过 beanName 获取该单例 Bean 实例
Object getSingleton(String beanName);
// 通过 beanName 判断该单例 Bean 实例是否存在
boolean containsSingleton(String beanName);
// 返回所有单例 Bean 的名称
String[] getSingletonNames();
// 返回已注册的单例 Bean 实例数量
int getSingletonCount();
// 返回当前使用的单例锁,主要提供给外部协作者使用
Object getSingletonMutex();
}
这个接口的核心实现类是 DefaultSingletonBeanRegistry,该类不仅实现了这些基础方法,还针对单例 Bean 扩展了许多功能,如:存储 Bean 之间的依赖关系、存储 Bean 的包含关系(外部类包含内部类)、获取 Bean 所处的状态(正在创建、创建完毕等)、回调销毁 Bean 时触发的 destroy 方法等。
下面是 DefaultSingletonBeanRegistry 类中的核心属性和方法:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/********** 1、定义的一些 Map 属性,用来保存单例 Bean 实例、 Bean 的依赖关系 **********/
// 缓存单例 Bean 实例,Key 是 beanName,Value 是单例 Bean 实例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 缓存 Bean 对应的 ObjectFactory
// ObjectFactory 是获取 Bean 实例的工厂,只不过这里获取的 Bean 还未完全实例化,属于提早暴露的 Bean
// 该属性在解决循环依赖时使用,后续会深入讨论
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 缓存 singletonFactories 属性中通过 ObjectFactory 创建的 Bean
// 该属性也是在解决循环依赖时使用,后续会深入讨论
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 保存已注册的单例 Bean 名称
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
// 保存当前正在创建的 Bean 的名称
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
// 保存当前从创建检查中排除的 Bean 的名称
private final Set<String> inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
...
// 当前 Bean 是否处于销毁状态
private boolean singletonsCurrentlyInDestruction = false;
// 保存实现了 DisposableBean 接口的 Bean,在销毁 Bean 时,会回调该 Bean 中的 destory 方法
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
// 保存 Bean 的包含关系,key 是 Bean 的名称,value 是 Bean 里面包含的其它 Bean 名称集合
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
// 保存 Bean 的依赖关系:key 是 Bean 的名称,value 是依赖于该 Bean 的其它 Bean 名称集合
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
// 保存 Bean 的依赖关系:key 是 Bean 的名称,value 是该 Bean 所依赖的其它 Bean 名称集合
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
/******************************** 2、注册单例 Bean 实例及对应的实例工厂 ********************************/
// 注册单例 Bean 实例
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "Bean name must not be null");
Assert.notNull(singletonObject, "Singleton object must not be null");
synchronized (this.singletonObjects) {
// 通过 beanName 获取 Map 中对应的单例 Bean 实例
Object oldObject = this.singletonObjects.get(beanName);
// 如果不为空,则抛出异常,因为单例已经存在,无法再次注册
if (oldObject != null) {
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
// 为空,则进入 addSingleton 方法
addSingleton(beanName, singletonObject);
}
}
// 缓存单例 Bean 实例
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 将单例 Bean 实例存放至 singletonObjects 集合
this.singletonObjects.put(beanName, singletonObject);
// 当 beanName 对应的 Bean 实例已被存放至 singletonObjects 集合时,singletonFactories
// 和 earlySingletonObjects 集合则不能再持有 beanName 对应的 ObjectFactory 和实例
// 其中原因会在后续循环依赖的文章深入讨论
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
// 存储 Bean 名称
this.registeredSingletons.add(beanName);
}
}
// 缓存 Bean 对应的 ObjectFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
/********************************* 3、获取单例 Bean 实例 *********************************/
@Override
public Object getSingleton(String beanName) {
// 该方法较为复杂,在后续结合循环依赖的场景讨论
}
...
/***************************** 4、对单例 Bean 实例的基础操作 *****************************/
// 删除单例 Bean 实例
protected void removeSingleton(String beanName) {
synchronized (this.singletonObjects) {
this.singletonObjects.remove(beanName);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.remove(beanName);
}
}
// 判断 beanName 对应的单例 Bean 实例时候存在
@Override
public boolean containsSingleton(String beanName) {
return this.singletonObjects.containsKey(beanName);
}
// 返回所有单例 Bean 的 beanName
@Override
public String[] getSingletonNames() {
synchronized (this.singletonObjects) {
return StringUtils.toStringArray(this.registeredSingletons);
}
}
// 返回单例 Bean 实例数量
@Override
public int getSingletonCount() {
synchronized (this.singletonObjects) {
return this.registeredSingletons.size();
}
}
...
/*************************************** 5、 Bean 的状态 **************************************/
// beanName 对应的 Bean 是否处于实例化阶段
public boolean isCurrentlyInCreation(String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
return (!this.inCreationCheckExclusions.contains(beanName) && isActuallyInCreation(beanName));
}
protected boolean isActuallyInCreation(String beanName) {
return isSingletonCurrentlyInCreation(beanName);
}
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
// 单例 Bean 实例化前执行,将正要创建的 Bean 加入 singletonsCurrentlyInCreation 集合
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
// 单例 Bean 实例化后执行,从 singletonsCurrentlyInCreation 集合中移除已创建的 Bean
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
...
/********************* 6、 存储 Bean 之间的关系、判断 Bean 之间的关系 *********************/
// 保存具有包含关系的 Bean(内部类与外部类)
public void registerContainedBean(String containedBeanName, String containingBeanName) {
synchronized (this.containedBeanMap) {
Set<String> containedBeans =
this.containedBeanMap.computeIfAbsent(containingBeanName, k -> new LinkedHashSet<>(8));
if (!containedBeans.add(containedBeanName)) {
return;
}
}
registerDependentBean(containedBeanName, containingBeanName);
}
// 保存具有依赖关系的 Bean
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
if (!dependentBeans.add(dependentBeanName)) {
return;
}
}
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}
...
/***************************** 7、 销毁 Bean 的方法 *****************************/
...
}
DefaultSingletonBeanRegistry 类中的属性及方法虽然很多,但也有规律可循的,大致分为对单例 Bean 实例的操作、管理 Bean 之间关系、针对 Bean 的不同状态进行操作及销毁 Bean 的操作。
该类中的核心还是那些 Map,类中的所有方法都是对这些 Map 进行操作,而这些 Map 中存储的是不同场景下的单例 Bean 。
最后
关于 SingletonBeanRegistry 就介绍到这,其主要还是针对单例 Bean 进行操作,外部调用者统一继承该类操作单例 Bean,其主要调用者还是 DefaultListableBeanFactory,前篇文章也说过,这是我们当前上下文环境中使用的 BeanFactory 工厂类,在工厂类中执行 getBean 操作时,会调用这些方法,后续会详细讨论 getBean 操作。最后值得注意是,Spring 也是在该类中解决循环依赖问题,这部分也会在后面详细讨论。
Spring(六)核心容器 - 注册单例 Bean 实例、SingletonBeanRegistry 简介的更多相关文章
- Spring IOC(三)单例 bean 的注册管理
Spring IOC(三)单例 bean 的注册管理 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 在 Spring 中 ...
- spring中IOC容器注册和获取bean的实例
spring中常用的功能主要的是ioc和aop,此处主要说明下,实例注册和使用的方法,此为学习后的笔记记录总结 1.使用xml文件配置 在idea中创建maven工程,然后创建实例Person,然后在 ...
- spring中如何向一个单例bean中注入非单例bean
看到这个题目相信很多小伙伴都是懵懵的,平时我们的做法大都是下面的操作 @Component public class People{ @Autowired private Man man; } 这里如 ...
- spring静态工厂方法得到单例bean
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationConte ...
- Spring(五)核心容器 - 注册 Bean、BeanDefinitionRegistry 简介
目录 前言 正文 1.BeanDefinitionRegistry 简介 2.registerBeanDefinition 方法注册 Bean 最后 前言 上篇文章我们对 BeanDefinition ...
- Spring单例Bean和线程安全
Spring的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢?例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框 ...
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
- 【Spring源码分析】非懒加载的单例Bean初始化过程(下篇)
doCreateBean方法 上文[Spring源码分析]非懒加载的单例Bean初始化过程(上篇),分析了单例的Bean初始化流程,并跟踪代码进入了主流程,看到了Bean是如何被实例化出来的.先贴一下 ...
随机推荐
- ApacheHudi常见问题汇总
欢迎关注公众号:ApacheHudi 1. ApacheHudi对个人和组织何时有用 如果你希望将数据快速提取到HDFS或云存储中,Hudi可以提供帮助.另外,如果你的ETL /hive/spark作 ...
- samba服务器红帽5.4搭建,亲测可用!!!
samba服务器搭建 服务器的环境 红帽5.4 vm15 挂载光盘 mount mount -t iso9660 设备目录 /mnt 表示挂载 软件包安装 samba服务器只需安装两个软件包,先找到软 ...
- Jmeter基础学习-下载及安装
1. Jmeter下载路径:http://jmeter.apache.org/download_jmeter.cgi 进入Jmeter下载界面后英语不好+技术不灵的同学会蒙圈,下载那个呢? *Bina ...
- 快速开发一个npm包(轮子)
动机 很多人都想写一个自己的轮子,可是开始动手的时候你总会遇到以下问题 一个基本的 js 库应该如何编写 基本的前端项目都要哪些文件 又要怎么打包发布到 npm 上 你的 es6 语法如何才能让别人识 ...
- 动态规划最短路径LintcodeNO110
动态规划最短路径LintcodeNO110 简单的dp题,没啥好说的... class Solution { public: /** * @param grid: a list of lists of ...
- 【转】在Ubuntu下建立Eclipse的Android开发环境
本文将介绍如何建立Ubuntu下基于Eclipse的Android开发环境的方法. 大部分的Android开发者都是使用Eclipse来开发Android,本文将向各位介绍一下建立Ubuntu下基于E ...
- 每天玩转3分钟 MyBatis-Plus - 2. 普通查询
每天玩转3分钟 MyBatis-Plus - 1. 配置环境 每天玩转3分钟 MyBatis-Plus - 2. 普通查询 mybatis-plus的查询功能非常强大, 这一篇,我们来看下mybati ...
- python爬虫——requests库使用代理
在看这篇文章之前,需要大家掌握的知识技能: python基础 html基础 http状态码 让我们看看这篇文章中有哪些知识点: get方法 post方法 header参数,模拟用户 data参数,提交 ...
- 内置3D对象-Unity3D游戏开发培训
内置3D对象-Unity3D游戏开发培训 作者:Jesai 2018-02-12 19:21:58 五大面板: -Hierachy:当前场景中的物体 图 1-1 -Project:项目中的所有资源 图 ...
- 我终于学会了使用python操作postgresql
一 前言 这篇文章不仅适合pgsql,更适合mysql,思路都是一致的,如果读者学会使用psycopg2操作pgsql,那么使用PyMySQL 操作mysql也是很简单:本篇文章涵盖内容广泛,提供的操 ...