介绍

在spring源码中,beanDefinition是一个最为重要的知识点之一,在源码中,会有一个merge的操作,将beanDefinition进行合并
org.springframework.beans.factory.support.AbstractBeanFactory#getMergedLocalBeanDefinition

在说mergeBeanDefinition之前,有一个概念,要先说清楚,就是在早期的版本中,有rootBeanDefinition和childBeanDefinition的概念,简单来说,就是一个beanDefinition的属性可以被继承,比如,一些公共的数据,可以放到rootBeanDefinition中,然后子beanDefinition直接继承rootBeanDefinition即可,多个子BeanDefinition无需多次在自己的beanDefinition设置公共属性,所以,我认为mergeBeanDefinition是为了兼容老版本的rootBeanDefinition,然后做的一个处理

在现在的源码中,通过自动扫描获取到的beanDefinition,是ScannedGenericBeanDefinition,也就是genericBeanDefinition的子类,那对于原来的rootBeanDefinition和childBeanDefinition是如何进行处理的呢?

应用

下面的这个例子,是我自己写的一个demo

@Configuration
@ComponentScan("com.spring.study.beandefinition")
public class BeanDefinitionConfig {}public class RootBeanEntity {private String name;private String type;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getType() {return type;}public void setType(String type) {this.type = type;}public void testRoot() {System.out.println("name:" + name + "\t" + "type:" + type);}
}public class UserEntity {private String name;private String type;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getType() {return type;}public void setType(String type) {this.type = type;}public void test() {System.out.println(name + "===" + type);}
}@Component
public class UserService {}public class BeanDefinitionTest {public static void main(String[] args) {AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();ac.register(BeanDefinitionConfig.class);/*** 初始化一个rootBeanDefinition*/RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();rootBeanDefinition.getPropertyValues().add("name","testName");rootBeanDefinition.getPropertyValues().add("type","typeTest");rootBeanDefinition.setBeanClass(RootBeanEntity.class);ac.registerBeanDefinition("rootBeanEntity",rootBeanDefinition);/*** 将genericBeanDefinition的父bd指向上面的rootBeanDefinition* 如果在genericBeanDefinition中覆盖了父bd中的属性,在打印的时候,就会取覆盖的值,如果未覆盖,则使用父bd的*/GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();genericBeanDefinition.setBeanClass(UserEntity.class);genericBeanDefinition.getPropertyValues().add("name","genBeanName");genericBeanDefinition.setParentName("rootBeanEntity");ac.registerBeanDefinition("userEntity",genericBeanDefinition);ac.refresh();ac.getBean(RootBeanEntity.class).testRoot();ac.getBean(UserEntity.class).test();System.out.println("===============================");System.out.println("通过@Configuration和@ComponentScan注入的bean是不同的beanDefinition类型");/*** class org.springframework.context.annotation.ScannedGenericBeanDefinition* ScannedGenericBeanDefinition* class org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition* AnnotatedGenericBeanDefinition** spring在扫描bean的时候,将包下所有的.class扫描到之后,会为每个class创建一个ScannedGenericBeanDefinition,所以,@Component注解对应* 的bean都是Scanned类型的** 加了@Configuration注解的bean,是在ac.registry()的时候,添加到beanDefinitionMap的,这时候,使用的是 AnnotatedBeanDefinitionReaders* 所以,对于配置类是AnnotatedGenericBeanDefinition*/System.out.println(ac.getBeanDefinition("userService").getClass());System.out.println(ac.getBeanDefinition("userService").getClass().getSimpleName());System.out.println(ac.getBeanDefinition("beanDefinitionConfig").getClass());System.out.println(ac.getBeanDefinition("beanDefinitionConfig").getClass().getSimpleName());}
}

最终打印的结果是:

name:testName    type:typeTest
genBeanName===typeTest
===============================
通过@Configuration和@ComponentScan注入的bean是不同的beanDefinition类型
class org.springframework.context.annotation.ScannedGenericBeanDefinition
ScannedGenericBeanDefinition
class org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
AnnotatedGenericBeanDefinitionProcess finished with exit code 0

源码

/*** 这里大致的意思是:*   1.在beanDefinition进行了合并之后,会存入到mergedBeanDefinitions这个集合中*     2.如果从map中获取到了合并之后的bd,那就不需要再次进行解析了*  3.如果获取不到,就重走一遍merge的步骤* @param beanName* @return* @throws BeansException*/
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {// Quick check on the concurrent map first, with minimal locking.RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);if (mbd != null) {return mbd;}return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
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.//1.containingBd 这个参数从上层调用过来,在代码中写死的null;如果为null,就从map中判断一下,是否已经存在if (containingBd == null) {mbd = this.mergedBeanDefinitions.get(beanName);}/*** 在5.2之后的源码中,这里的判断加了一个stale的判断,这个标识的意思是:定义是否需要合并bean** 这里的mbd变量需要注意:*     不管bd是RootBeanDefinition还是GenericBeanDefinition,都会强转成RootBeanDefinition(mbd)*/if (mbd == null) {/*** 2.判断当前bd是否有设置parentName*   如果当前bd没有设置parentName,我们姑且可以认为,这个bd就是父bd*  下面的判断可以看到:如果bd是RootBeanDefinition,就clone一个新的bd*  如果bd不是RootBeanDefinition,那就根据bd,new一个RootBeanDefinition对象**/if (bd.getParentName() == null) {// Use copy of given root bean definition.if (bd instanceof RootBeanDefinition) {mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();}else {mbd = new RootBeanDefinition(bd);}}else {// Child bean definition: needs to be merged with parent./*** 3.如果进入到这里,说明当前bd设置了parentName属性*/BeanDefinition pbd;try {/*** 3.1 这里的意思,我的理解是:递归获取到bd的parent,直到获取到最后一个bd*/String parentBeanName = transformedBeanName(bd.getParentName());if (!beanName.equals(parentBeanName)) {pbd = getMergedBeanDefinition(parentBeanName);}else {/*** 3.2 如果beanName和parentBeanName一样?这个场景是什么?* 根据parentName获取到parentBeanDefinition*/BeanFactory parent = getParentBeanFactory();if (parent instanceof ConfigurableBeanFactory) {pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);}else {throw new NoSuchBeanDefinitionException(parentBeanName,"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +"': cannot be resolved without an AbstractBeanFactory 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 new一个RootBeanDefinition* 第二行代码是:将bd(可以理解为子bd)的属性赋值到mbd中,子类的属性会覆盖父类的属性,* 方法中,就是通过一堆的set进行属性覆盖** 如果bd本身就是RootBeanDefinition,那就不会执行这里的逻辑*/mbd = new RootBeanDefinition(pbd);mbd.overrideFrom(bd);}// Set default singleton scope, if not configured before./*** 如果bd没有设置scope,默认是singleton*/if (!StringUtils.hasLength(mbd.getScope())) {mbd.setScope(RootBeanDefinition.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()) {/*** 将合并后的bd存入到map集合中,spring在后面初始化bean的流程中,就无需再次merge,直接从这个map中根据beanName获取即可*/this.mergedBeanDefinitions.put(beanName, mbd);}}return mbd;}
}

这里的注释也加的比较清楚,就不做过多的解释了,大致可以分为两种情况
1、如果当前beanDefinition没有设置parent属性,那就是一个rootBeanDefinition,无需进行其他的处理
2、如果当前beanDefinition设置了parent属性,需要先将父beanDefinition的属性获取到,然后再将自己的beanDefinition的属性覆盖到父beanDefinition中
3、最后将得到的rootBeanDefinition存入到缓存中

总结

总结来说,
1.如果当前子beanDefinition有设置rootBeanDefinition,那在初始化子beanDefinition的时候,会合并rootBeanDefinition的属性,然后通过一些列的set方法,将子beanDefinition的属性覆盖rootBeanDefinition中的属性
2.如果当前beanDefinition没有设置parentBeanDefinition属性,那就表示当前beanDefinition本身就是rootBeanDefinition,那就只需要根据beanDefinition创建一个rootBeanDefinition,无需考虑属性覆盖的问题
3.在merge了之后,会将merge之后的beanDefinition放入到集合缓存中,spring源码中有多个位置会对beanDefinition进行merge操作,那放入到缓存之后,就无需每次去进行merge
4.所以我认为mergeBeanDefinition的这个操作,是为了兼容原来版本中rootBeanDefinition和childBeanDefinition这种设置方式,在我毕业工作之后,还没有遇到过项目是通过rootBeanDefinition和childBeanDefinition来设置属性的

mergeBeanDefinition原理相关推荐

  1. UUID的使用及其原理

    今天敲项目要用UUID,想起之前老师告诉UUID的使用,但没说具体的生成逻辑,于是我进行了百度 首先,UUID的使用: //生成随机的UUID String uuid = UUID.randomUUI ...

  2. etcd 笔记(01)— etcd 简介、特点、应用场景、常用术语、分布式 CAP 理论、分布式原理

    1. etcd 简介 etcd 官网定义: A highly-available key value store for shared configuration and service discov ...

  3. git原理及常见使用方法

    Git 原理入门-来自阮一峰 Git 是最流行的版本管理工具,也是程序员的必备技能之一. 即使天天使用它,很多人也未必了解它的原理.Git 为什么可以管理版本?git add.git commit这些 ...

  4. 微机原理—定时计数控制接口

    别看题目很高深,其实就是很简单的定时器和计数器而已. 通常用手机定个闹钟,就是定时器的使用. 工厂里通过传送带上安装传感器,传感器传输给计算机的信号用来计数. 这是一些很简单的应用,通过很小的一个芯片 ...

  5. 三层交换机原理:01路由器如何隔离广播域?

    前言: 当网络规模较大的时候,需要设备来隔离广播域,防止网络中因产生广播风暴而导致网络效率降低,而二层交换机不能隔离广播域,所以需要三层路由器设备来隔离广播域! 但三层路由器为什么能够隔离广播域,是如 ...

  6. CRF(条件随机场)与Viterbi(维特比)算法原理详解

    摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...

  7. BiLSTM-CRF学习笔记(原理和理解) 维特比

    https://www.zhihu.com/question/20136144 维特比详解 BiLSTM-CRF 被提出用于NER或者词性标注,效果比单纯的CRF或者lstm或者bilstm效果都要好 ...

  8. 【Learning Notes】线性链条件随机场(CRF)原理及实现

    1. 概述 条件随机场(Conditional Random Field, CRF)是概率图模型(Probabilistic Graphical Model)与区分性分类( Discriminativ ...

  9. Jieba分词原理与解析

    1 HMM模型 马尔科夫过程: 以天气判断为例:引出隐马尔科夫模型 于是我们可以将这种类型的过程建模为有一个隐藏的马尔科夫过程和一个与这个隐藏马尔科夫过程概率相关的并且可以观察到的状态集合.这就是本文 ...

  10. 自然语言处理课程(二):Jieba分词的原理及实例操作

    上节课,我们学习了自然语言处理课程(一):自然语言处理在网文改编市场的应用,了解了相关的基础理论.接下来,我们将要了解一些具体的.可操作的技术方法. 作为小说爱好者的你,是否有设想过通过一些计算机工具 ...

最新文章

  1. 华理c语言设计网上作业,华东理工大学第一学年第二学期网上作业参考答案C语言设计1...
  2. 信息系统项目管理师:第1章:信息化与信息系统(2)-重点汇总
  3. Visual Studio集成Qt环境搭建_详解与测试
  4. 28款网页CSS设计工具和生成器
  5. yum仓库、源以及编译安装笔记
  6. 如何使用eclipse进行嵌入式Linux的开发
  7. 【多线程】LockSupport 使用 原理 源码 分析
  8. Doker : Docker 容器与宿主机共享文件
  9. python3未配置_python3安装文件遇到ssl未安装问题
  10. 【ffmpeg】overlay带有透明通道的视频
  11. vue对象中的实例属性
  12. 等概率随机产生0和1
  13. gitlab hook declined错误
  14. 微型计算机主机的主要部件,微型机主机的主要部件
  15. 全国大学生物联网设计竞赛作品 | 室内消毒机器人-艾盾
  16. GNU 和 UNIX 命令
  17. TIA博途WINCC的触摸屏VB脚本入门学习(IF THEN ELSE判断语句)
  18. php好趣网抓取_PHP抓取卫视直播源
  19. hihoCoder - 1353 满减优惠
  20. docker 中 --privileged 参数

热门文章

  1. 物体检测算法:R-CNN,SSD,YOLO 动手学深度学习v2 pytorch
  2. 翻译: 3.3. 线性回归的简明实现 pytorch
  3. 极客大学产品经理训练营 产品经理新人如何落地 第0课总结
  4. iOS app 启动 crash XCode 11 NSPOSIXErrorDomain Code=2 “No such file or directory“
  5. 算法:Find First and Last Position of Element in Sorted Array(在有序数组中搜索第一个和最后一个找到的数字)
  6. php xml构造,C++_C语言实现xml构造解析器,纯C实现xml构造解析器,所有实 - phpStudy...
  7. wide and deep及其发展
  8. Deep Learning for Joint Channel Estimation and Feedback in Massive MIMO Systems
  9. 仿射组合(Affine Combination)的定义与性质
  10. 483. Smallest Good Base 1