Spring(五)核心容器 - 注册 Bean、BeanDefinitionRegistry 简介
前言
上篇文章我们对 BeanDefinition 进行了讨论,BeanDefinition 是对 Bean 的定义,其保存了 Bean 的各种信息,如属性、构造方法参数、是否单例、是否延迟加载等。这里的注册 Bean 是指将 Bean 定义成 BeanDefinition,之后放入 Spring 容器中,我们常说的容器其实就是 Beanfactory 中的一个 Map,key 是 Bean 的名称,value 是 Bean 对应的 BeanDefinition,这个注册 Bean 的方法由 BeanFactory 子类实现。
注:本篇文章使用的 SpringBoot 版本为 2.0.3.RELEASE,其 Spring 版本为 5.0.7.RELEASE
正文
在前面的《Spring(三)核心容器 - ApplicationContext 上下文启动准备》文章中说过,当前环境的 BeanFactory 实现类是 DefaultListableBeanFactory,是一个具有注册功能的完整 Bean 工厂,注册 Bean 的方法是 registerBeanDefinition,DefaultListableBeanFactory 通过实现 BeanDefinitionRegistry 接口,重写该方法。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
...
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
...
}
...
}
讨论 registerBeanDefinition 方法之前,先来简单介绍 BeanDefinitionRegistry 接口。
1、BeanDefinitionRegistry 简介
BeanDefinitionRegistry 是一个接口,它定义了关于 BeanDefinition 的注册、移除、查询等一系列的操作。
public interface BeanDefinitionRegistry extends AliasRegistry {
// 注册 BeanDefinition
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
// 移除 BeanDefinition
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 获取 BeanDefinition
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 根据 beanName 判断容器是否存在对应的 BeanDefinition
boolean containsBeanDefinition(String beanName);
// 获取所有的 BeanDefinition
String[] getBeanDefinitionNames();
// 获取 BeanDefinition 数量
int getBeanDefinitionCount();
// 判断 beanName 是否被占用
boolean isBeanNameInUse(String beanName);
}
该接口有三个实现类:DefaultListableBeanFactory、GenericApplicationContext、SimpleBeanDefinitionRegistry,其中 GenericApplicationContext 底层调用的是 DefaultListableBeanFactory 中的实现方法,所以严格意义上来说,只有两个实现类。这里,我们主要讨论 DefaultListableBeanFactory 中的方法实现。
2、registerBeanDefinition 方法注册 Bean
前面说过 registerBeanDefinition 方法的主要实现类是 DefaultListableBeanFactory :
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
...
// 存储所有的 BeanDefinition ,key 是 Bean 的名称。我们一直称呼的容器,底层就是这个 Map
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 存储所有 Bean 名称
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
// 存储手动注册的单例 Bean 名称
private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);
// 存储冻结的 BeanDefinition,留作后面缓存用
private volatile String[] frozenBeanDefinitionNames;
...
// 方法的入参为 Bean 名称和对应的 BeanDefinition
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
// 如果 beanDefinition 的实例为 AbstractBeanDefinition,则进行验证
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 验证:
// 如果有重写方法,但是是工厂方法,则抛出异常,因为重写方法需要代理,而工厂方法无法代理;
// 通过方法名称,判断 Bean 中该名称方法存在的数量,0:方法不存在,报错;1:方法非重载,overloaded 属性设为 false;
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
// 先从 beanDefinitionMap 中尝试获取 beanName 对应 BeanDefinition
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 不为 null,则 beanName 对应的 BeanDefinition 已经存在
if (oldBeanDefinition != null) {
// 是否应允许覆盖 BeanDefinition,不允许则抛异常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
/***************************** 若允许覆盖 *****************************/
// 判断 Bean 的角色大小:
// 0:用户定义的 Bean、1:来源于配置文件的 Bean、2:Spring 内部的 Bean;
// 当原 BeanDefinition 角色小于新的 BeanDefinition 角色时,输出一个 warn 日志,提示 BeanDefinition 被覆盖
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
// 当新 BeanDefinition 属性值不等于原 BeanDefinition 属性值时,输出 info 提示信息
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 最后,输出 debug 日志信息:用等效的新 BeanDefinition 覆盖原 BeanDefinition
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 添加至 BeanDefinition 集合,并覆盖原 BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// Map 中无对应的 BeanDefinition,则直接注册
else {
// 已开始创建 Bean
if (hasBeanCreationStarted()) {
synchronized (this.beanDefinitionMap) {
// 将 Bean 对应的 BeanDefinition 放入 beanDefinitionMap 中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 创建新的 beanNames 集合,并将已缓存的 beanName 和新的 beanName 加入该集合
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
// 在手动注册 Bean 的集合中,如果存在同名的 beanName,则将集合中同名的 beanName 删除
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
// 仍处于启动注册阶段
else {
// 将当前 Bean 对应的 BeanDefinition 放入 beanDefinitionMap 中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 将当前 beanName 放入 beanDefinitionNames
this.beanDefinitionNames.add(beanName);
// 删除手动注册 Bean 集合中同名的 beanName
this.manualSingletonNames.remove(beanName);
}
// 将存储冻结 BeanDefinition 的 Map 置为 null
this.frozenBeanDefinitionNames = null;
}
// 当前注册的 BeanDefinition 已在 beanDefinitionMap 中存在,或者其实例已在存储单例 Bean 的 Map 中存在
if (oldBeanDefinition != null || containsSingleton(beanName)) {
// 重置 BeanDefinition,主要做一些清理工作
resetBeanDefinition(beanName);
}
}
}
执行完 registerBeanDefinition 方法后,Bean 的名称和对应的 BeanDefinition 就被放入了容器中,后续获取 Bean 也是从这个容器中获取。
当然,DefaultListableBeanFactory 还实现了 BeanDefinitionRegistry 接口的其它方法,如对 BeanDefinition 进行移除、判断是否存在、获取数量等操作,其实都是围绕 beanDefinitionMap 这个 Map 进行的,这里就不详细介绍。
需要注意的是 registerBeanDefinition 方法会在后面频繁被调用,后续会逐一提到。
最后
这篇文章主要对注册 Bean 的核心方法进行讨论,但其中涉及到了单例 Bean 实例注册,这和当前文章的注册 Bean 不同,当前保存的是任意 Bean 的信息,而后者,保存的是单例 Bean 的对象,我们将在下篇文章详细讨论。
Spring(五)核心容器 - 注册 Bean、BeanDefinitionRegistry 简介的更多相关文章
- Spring之核心容器bean
摘要:Spring的核心容器实现了Ioc,其目 的是提供一种无侵入式的框架.在本文中,首先讲解了Spring的基础bean的相关知识,然后介绍了Spring是如何对bean进行管理的. 在Spring ...
- 【Spring】 Spring的核心容器
Spring的核心容器 文章目录 Spring的核心容器 BeanFactory ApplicationContext 1.通过ClassPathXmlApplicationContext创建 2.通 ...
- Spring(六)核心容器 - 注册单例 Bean 实例、SingletonBeanRegistry 简介
前言 上篇文章我们对注册 Bean 的核心类 BeanDefinitionRegistry 进行了讨论,这里的注册 Bean 是指保存 Bean 的相关信息,也就是将 Bean 定义成 BeanDef ...
- Spring为IOC容器注入Bean的五种方式
一 @Import导入组件,id默认是组件的全类名 //类中组件统一设置.满足当前条件,这个类中配置的所有bean注册才能生效: @Conditional({WindowsCondition.clas ...
- spring中IOC容器注册和获取bean的实例
spring中常用的功能主要的是ioc和aop,此处主要说明下,实例注册和使用的方法,此为学习后的笔记记录总结 1.使用xml文件配置 在idea中创建maven工程,然后创建实例Person,然后在 ...
- Spring学习总结(6)——Spring之核心容器bean
一.Bean的基础知识 1.在xml配置文件中,bean的标识(id 和 name) id:指定在benafactory中管理该bean的唯一的标识.name可用来唯一标识bean 或给bean起别名 ...
- 手撸Spring框架,设计与实现资源加载器,从Spring.xml解析和注册Bean对象
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你写的代码,能接的住产品加需求吗? 接,是能接的,接几次也行,哪怕就一个类一片的 i ...
- Spring IOC 一——容器装配Bean的简单使用
下文:SpringIOC 二-- 容器 和 Bean的深入理解 写在前面 这篇文章去年写的,缘起于去年某段时间被领导临时"抓壮丁"般的叫过去做java开发,然后在网上找了一个 Sp ...
- Spring总结_04_容器和bean
一.概念理解 1.容器 IoC容器负责容纳并管理bean,在Spring中,BeanFactory是IoC容器的核心接口. 它的职责包括:实例化.定位.配置应用程序中的对象及建立这些对象间的依赖. ...
随机推荐
- 【题解】BZOJ4548 小奇的糖果(树状数组)
[题解]BZOJ4548 小奇的糖果(树状数组) 说在前面:我有个同学叫小奇,他有一个朋友叫达达,达达特爱地理和旅游,初中经常AK地理,好怀恋和他已经达达一起到当时初中附近许多楼盘的顶楼逛的时光... ...
- 洛谷$P$3301 $[SDOI2013]$方程 $exLucas$+容斥
正解:$exLucas$+容斥 解题报告: 传送门! 在做了一定的容斥的题之后再看到这种题自然而然就应该想到容斥,,,? 没错这题确实就是容斥,和这题有点儿像 注意下的是这里的大于和小于条件处理方式不 ...
- linux solr7.2+tomcat8 详细部署整合
1.去solr官网下solr-7.2.0.tgz 2.上传至linux解压 tar -zxvf solr-7.2.0.tgz 3.准备tomcat8 拷贝solr-7.2.0/server/solr- ...
- 「Luogu P2278」[HNOI2003]操作系统 解题报告
题面 一道模拟题,模拟CPU的处理过程?!省选模拟题 思路: 模拟退火大法+优先队列乱搞 要注意的点 1.空闲时,CPU要处理进程 2.当队列中没有进程时,要先进行判断,然后访问 3.当优先级高的进程 ...
- 1034 有理数四则运算 (20 分)C语言
题目描述 本题要求编写程序,计算2个有理数的和.差.积.商. 输入描述: 输入在一行中按照"a1/b1 a2/b2"的格式给出两个分数形式的有理数,其中分子和分母全是整型范围内的整 ...
- Spark学习笔记(三)—— Standalone模式
上篇笔记记录了Local模式的一些内容,但是实际的应用中很少有使用Local模式的,只是为了我们方便学习和测试.真实的生产环境中,Standalone模式更加合适一点. 1.基础概述 Standalo ...
- 【转】13个JavaScript图表(JS图表)图形绘制插件
现在网络上又有越来越多的免费的(JS 图表)JavaScript图表图形绘制插件.我之前给一家网站做过复杂的图形,我们用的是 highchart.在那段时间,没有很多可供选择的插件.但现在不同了,很容 ...
- Numpy常用方法及应用总汇
目录 Numpy 1.基本操作 1.1数组转换 1.2数组生成 1.3文件读取 1.4查看操作 2.数据类型 2.1指定数据类型: 2.2查看数据类型 2.3数据类型转换 3.数组运算 3.1数组间运 ...
- floj 2264
2.公路建设(highway.c/cpp/pas) 在滨海市一共有n 个城市,编号依次为1到n,它们之间计划修建m条双向道路,其中修建第i条道路的费用为ci. 海霸王作为滨海市公路建设项目的总工程师, ...
- mac-air上安装 rabbitmq 并简单使用
简介: brew 安装 rabbitmq,docker安装rabbitmq 安装官方php-amqp 扩展 简单使用样例(发送10次helloworld