文章目录

  • 一、BeanDefinition
    • 1、具体实现子类
    • 2、手动创建子类
    • 3、beanClass成员变量的含义
  • 二、BeanDefinitionMap
  • 三、RootBeanDefintion
    • 1、具体获取的流程

通过本文你将收获:

  1. RootBeanDefintion、BeanDefinition、BeanDefinitionMap三者的关系即其基本含义

  2. BeanDefinition接口的实现子类

  3. BeanDefinition子类中beanClass成员变量的含义

  4. 根据beanName获取RootBeanDefintion的流程


一、BeanDefinition

BeanDefinition:Bean定义的接口,在Spring中该接口有许多实现类如下图。不同的BeanDefinition的实现类也代表着不同的含义。

1、具体实现子类

  1. 如果使用配置类的方式启动Spring容器,那么我们就会生成AnnotatedGenericBeanDefinition
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
  1. 在实例化Bean之前,Spring需要先去加载自己的一些内部的Bean,比如@Configuration对应的Bean,此时使用的是RootBeanDefinition

  2. 当我们通过包路径去扫描Bean时,扫描出来的Bean定义使用的是ScannedGenericBeanDefinition

观察上面的三种情况:

  1. 将配置类初始化为一个BeanDefinition的实现类
  2. 将@Configuration注解对应的类初始化为一个BeanDefinition的实现类
  3. 将扫描出来的符合要求Bean要求的类初始化为一个BeanDefinition的实现类

2、手动创建子类

同理,我们是不是也能添加BeanDefinition到Spring容器中?

于是你就可以看到如下的几步代码,该代码表示我们手动创建一个BeanDefinition的实现类,并将他添加到Spring容器中:

  1. 创建一个BeanDefinition
  2. 设置BeanDefinition的类型
  3. 将BeanDefinition添加到Spring容器中

3、beanClass成员变量的含义

点进AbstractBeanDefinition类的代码,我们会发现其beanClass的类型并非为Class类型,而是一个Object类型,按照前面Teacher类的描述,这个属性是用来标记对应Bean的类型的,难道直接使用Class类型不好吗?

@Nullable
private volatile Object beanClass;

其实beanClass这个属性最开始是一个String类型的类的全路径名字,后期Spring容器在初始化的时候会去校验这个属性是一个Class类型的类还是一个字符串,如果是字符串就通过将它解析为一个Class。

对应设置这个字段的位置在:

下面代码来自我们扫描包下符合要求的Bean时初始化的ScannedGenericBeanDefinition类的构造方法,给该方法传入类的元数据信息,对类元数据不熟悉的小伙伴可以参考:MetadataReader、ClassMetadata、AnnotationMetadata的简单使用

public ScannedGenericBeanDefinition(MetadataReader metadataReader) {Assert.notNull(metadataReader, "MetadataReader must not be null");this.metadata = metadataReader.getAnnotationMetadata();// 获取类的全路径字符串,设置到beanClass属性上,此处设置的是字符串setBeanClassName(this.metadata.getClassName());setResource(metadataReader.getResource());
}

对应解析这个字段的位置在:

AbstractBeanFactory类中

AbstractAutowireCapableBeanFactory类中

AbstractBeanFactory类中

  • 判断该属性是是否为一个类
  • 如果是一个类,表示该字段的值可以直接使用
  • 不是一个类,就去解析对应的字符串,通过解析为一个类

这也就是为什么我们直接设置为类也可以。

为了进一步验证我们的解释,我修改了我的测试代码:

我将对应的类设置为类的路径依然能够完成BeanDefinition的添加

需要测试成功还需要对类AbstractBeanDefinition进行一点小小的改动,因为原setBeanClass方法只能将参数设置为Class类型,我自己添加了一个设置为字符串属性的方法


二、BeanDefinitionMap

看名字也知道是存放BeanDefinition的集合,此时的Bean还只是一个雏形,仅仅包含Bean的一些简单基本信息

类比理解单例池和Bean的关系。

前面铺垫了那么多的BeanDefinition的知识,就是后面你能更好的理解BeanDefinitionMap。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

当Spring扫描到了符合要求的类时,都会将类实例化为一个BeanDefinition,然后再将BeanDefinition添加到BeanDefinitionMap,即类似下面这样的代码(Spring中有很多地方都有这样的代码):

registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

调用registerBeanDefinition方法,参数为:Bean的名字,通过类实例化BeanDefinition的实现类(具体有哪些实现类上面已经进行了详细的说明)。

registry对象有三个实现,但其实GenericApplicationContext和DefaultListableBeanFactory最终会调用同一个方法,所以只会有两个不同的实现类SimpleBeanDefinitionRegistry和DefaultListableBeanFactory:

这个registry对象可以简单的理解为Spring容器,即表示我们将BeanDefinition注册到哪个容器(Spring容器有很多种实现)BeanDefinitionMap中。


三、RootBeanDefintion

这是BeanDefintion(后文再出现BeanDefintion以BD代替)众多实现中的一种

也是众多BD的归宿,无论你前面的BD是什么类型,在实例化Bean的时候,会去判断Bean的一些要素情况,此时会将所有的BD都转化为RootBD,继而完成接下来的操作

private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);

这个转化的逻辑就是去mergedBeanDefinitions集合中获取(类比理解BDMap,只是前者存的是BD接口,后者存的是BD的子类RootBD类)

  • 如果对应的mergedBeanDefinitions中有就直接返回
  • 如果没有就去BDMap中获取,然后转化为mergedBeanDefinitions中的数据

总结起来就是:Spring去扫描符合要求的类的时候,会将符合要求的类实例化为各种类型的BD(本文开头有说明),但是当我们去实例化所有非懒加载Bean的时候(完成Bean的生命周期),会将所有的BD类型进行统一,全部转化成RootBD,用于统一判断对应的BD信息

1、具体获取的流程

上面说到各种类型的BD转化为RootBD就是去对应的集合中获取,然后返回,但其实这个获取的过程还是比较复杂的。以下部分为单独对该过程进行的讲解。

在看代码逻辑之前,需要一些其他知识

补充知识点:

父子容器、父子Bean

在Spring中我们可以配置父子容器,用于加载两个容器中的Bean,SpringMVC中就利用了这个知识点。同样我们也可以配置父子Bean,代码如下

public class BianChengShiSpringTest2 {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigBean.class);AnnotationConfigApplicationContext context2 = new AnnotationConfigApplicationContext(ConfigBean2.class);// 构建父子容器context.setParent(context);AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();beanDefinition.setBeanClass(Teacher.class);AbstractBeanDefinition beanDefinition2 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();beanDefinition2.setBeanClass(MoBian.class);// 构建父子BeanbeanDefinition2.setParentName("teacher");context.registerBeanDefinition("teacher",beanDefinition);context.registerBeanDefinition("mobian",beanDefinition2);System.out.println(context.getBean("teacher"));System.out.println(context.getBean("mobian"));}
}

获取的详细的注释已经写在了代码里面

protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)throws BeanDefinitionStoreException {synchronized (this.mergedBeanDefinitions) {RootBeanDefinition mbd = null;// Check with full lock now in order to enforce the same merged instance.if (containingBd == null) {// 情况一// 根据beanName去mergedBeanDefinitions集合中获取对象mbd = this.mergedBeanDefinitions.get(beanName);}// 集合中没有,进入if(有直接返回)if (mbd == null) {// 判断BD是否存在父BDif (bd.getParentName() == null) {// Use copy of given root bean definition.// 在没有父Bean(我们通常定义的Bean都是没有父Bean的)的情况下,如果bd是RootBD类型,就克隆一份属性if (bd instanceof RootBeanDefinition) {// 情况二mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();}else {// 情况三// 如果不是RootBD,我们就将之前的BD属性传给RootBD,用于RootBD的数据初始化mbd = new RootBeanDefinition(bd);}}else {// Child bean definition: needs to be merged with parent.BeanDefinition pbd;try {// 进入此代码块,表示存在父Bean// 此处代码与实现BeanFactory接口的Bean实现有关,除此之外都是传进去的beanName等于返回的beanName// 有父Bean,就获取父Bean的名字String parentBeanName = transformedBeanName(bd.getParentName());if (!beanName.equals(parentBeanName)) {// 情况四// 如果父Bean的名字和自己不相同,就递归调用该方法,用于找到父Bean的数据pbd = getMergedBeanDefinition(parentBeanName);}else {// 如果父Bean的名字和自己相同,就获取父Spring容器// 同一个Spring容器中只能出现一个相同名字的Bean,所以该种情况出现时,父Bean只会存在于父容器中BeanFactory parent = getParentBeanFactory();if (parent instanceof ConfigurableBeanFactory) {// 情况五// 去父容器中递归进行获取BD数据信息pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);}// 除此之外就抛异常else {throw new NoSuchBeanDefinitionException(parentBeanName,"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +"': cannot be resolved without a ConfigurableBeanFactory parent");}}}catch (NoSuchBeanDefinitionException ex) {throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);}// Deep copy with overridden values.// 将父BD的内容覆盖给子BDmbd = new RootBeanDefinition(pbd);// 将子BD原有的数据再覆盖一次。两步总结起来就是子BD没有的属性使用父类的,有的属性就使用自己原有的mbd.overrideFrom(bd);}// Set default singleton scope, if not configured before.if (!StringUtils.hasLength(mbd.getScope())) {mbd.setScope(SCOPE_SINGLETON);}// A bean contained in a non-singleton bean cannot be a singleton itself.// Let's correct this on the fly here, since this might be the result of// parent-child merging for the outer bean, in which case the original inner bean// definition will not have inherited the merged outer bean's singleton status.if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {mbd.setScope(containingBd.getScope());}// Cache the merged bean definition for the time being// (it might still get re-merged later on in order to pick up metadata changes)if (containingBd == null && isCacheBeanMetadata()) {this.mergedBeanDefinitions.put(beanName, mbd);}}return mbd;}
}

核心步骤总结成下面的导图逻辑,供参考:

  • 一定要明白父子容器和父子Bean的概念
  • 同一个容器中是不能出现相同名字的Bean的(从本质出发,Spring容器的单例池是一个map,当map出现两个相同的key时,数据是会覆盖的,自然就有问题)

浅谈BeanDefinition、BeanDefinitionMap、RootBeanDefintion三者的关系相关推荐

  1. python axes_浅谈matplotlib.pyplot与axes的关系

    最近在学习数据可视化,梳理一下其中一些诸如pandas绘图.matplotlib绘图.pyplot(plt).axes等概念. 重要的事情说三遍:axes不是axis!axes不是axis!axes不 ...

  2. 张钹院士:浅谈人工智能与大数据的关系

    来源:联合时报 本文共2385字,建议阅读5分钟. 本文主要浅谈了人工智能和大数据的关系以及中国在人工只能领域对世界的超越. 中国科学院院士张钹对国内外人工智能产业发展现状,提出我国仅靠跟随性的应用深 ...

  3. 浅谈BI和大数据的关系

    欢迎进QQ群讨论:672600296 ---------------------------------------------------------------------- 前言 最近发现很多人 ...

  4. 计算机的发展与教育的关系,浅谈人工智能与教育教学的关系

    作业内容 目前,人工智能的发展已经进入到白热化阶段,行各业都在人工智能领域寻求新的发展,智能导师系统(ITS) 是一门涉及人工智能(人工智能.AI).计算机科学.认知科学.教育.心理学和行为科学的综合 ...

  5. 浅谈数字经济新技术间的关系——云计算、物联网、大数据、区块链、人工智能、元宇宙

    1.什么是数字经济 数字经济是继农业经济.工业经济之后的主要经济形态,是以数据资源为关键要素,以现代信息网络为主要载体,以信息通信技术融合应用.全要素数字化转型为重要推动力,促进公平与效率更加统一的新 ...

  6. 实对称矩阵的性质_浅谈矩阵的相似对角化(一)

    森屿瑾年:浅谈线性变换和矩阵之间的关系​zhuanlan.zhihu.com 通过前面的讨论,我们引出了线性变换在不同基下的矩阵之间的关系,知道了线性变换在不同基下的矩阵是相似的,进而我们可以通过选取 ...

  7. 浅谈javascript中原型(prototype)、构造函数、对象实例及三者之间的关系

    转自:http://www.cnblogs.com/zhangwei412827/archive/2012/12/14/2816263.html 浅谈javascript中原型(prototype). ...

  8. 浅谈CPU、内存,硬盘三者关系

    浅谈CPU.内存,硬盘三者关 随着SSD的发展,只要电脑一卡顿,人们会第一想的是换SSD就可以升级电脑性能,其实不尽然.电脑卡顿跟固态硬盘固然有关系,但是它跟CPU和内存条的关系也很多,接下来带大家了 ...

  9. JDK与JRE的关系和path的作用浅谈

    JDK与JRE的关系和path的作用浅谈 摘要:JDK与JRE的关系以及path的配置对于初学者是要明白的问题,那么具体的JDK与JRE的关系是什么呢?那么本文讲给你简单介绍. 标签:JDK与JRE关 ...

  10. 浅谈UML类图中类之间的5种关系

    什么是UML类图? 类图显示了一组类.接口.协作以及他们之间的关系.在UML中问题域最终要被逐步转化,通过类来建模,通过编程语言构建这些类从而实现系统.类加上他们之间的关系就构成了类图,类图中还可以包 ...

最新文章

  1. simple-spring-memcached统一缓存的使用实例4
  2. (原创)Android6.0亮屏流程分析
  3. 【JAVA并发编程实战】1、对象的共享
  4. ios 隐藏app的插件_等了5年终于复活,iPhone上最干净好用的微博App
  5. 用XenoCode 2006 加密dll(.NET
  6. 关于request.getParameter(java.lang.String name)
  7. P3175 [HAOI2015]按位或
  8. Python+OpenCV:基于分水岭算法的图像分割(Image Segmentation with Watershed Algorithm)
  9. Chrome浏览器护眼插件
  10. ETERM操作和错误集合
  11. html设置线条颜色渐变,CSS3 - 设置渐变颜色背景,线性/放射性/循环(附在线渐变生成工具)...
  12. CnCerT.Net.SKiller工作原理
  13. [DataAnalysis]基于统计假设检验的机器学习模型性能评估——泛化误差率的统计检验
  14. 设CPU共有16根地址线,8根数据线,并用MREQ (低电平有效) .作访存控制信号,R/W作读写命令信号(高电平为读,,低电平为写)。
  15. Unity打包篇:能够解决Unity打包Gradle遇到的所有问题方法整合!(持续更新中!)
  16. docer中运行crontab
  17. 两轮电自2.0时代开启 小牛电动以独立主见创造新物种
  18. 吸血鬼数字java_Java求吸血鬼数算法(通用)
  19. 客户体验是什么?如何提升用户体验从而提高产品成单率?必读!
  20. Linux权限委派(生产环境必备)

热门文章

  1. LOJ2316「NOIP2017」逛公园
  2. java实践体会,java实践心得体会范文3篇
  3. python迭代器和生成器_python迭代器和生成器
  4. .dat文件写入byte类型数组_不可不知的可变Java长数组
  5. word打开文档很久很慢_word打开慢,教您怎么解决word打开慢
  6. 简述python2.x和python3.x的区别_python面试题Python2.x和Python3.x的区别
  7. java异常类型及处理
  8. java反序列化为空_Java序列化/反序列化,提供空对象引用
  9. Introduction to Computer Networking学习笔记(十四):网络中为什么使用packet switching
  10. dll封装成activex控件_Qt编写自定义控件26-平铺背景控件