#yyds干货盘点# Spring源码三千问BeanDefinition详解——什么是 RootBeanDefinition?merged bean definition 又是什么鬼?

Posted 老王学源码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#yyds干货盘点# Spring源码三千问BeanDefinition详解——什么是 RootBeanDefinition?merged bean definition 又是什么鬼?相关的知识,希望对你有一定的参考价值。

@TOC

前言

从接触 Spring 源码之后,我就对 BeanDefinition 早已耳熟能详,但一直没有深入研究过它的作用,以至于每次在读源码过程中碰到 BeanDefinition 时有种熟悉又陌生的感觉。
BeanDefinition 是 Spring IoC 体系中的一个重要概念,如果不能深入理解它,那么必定不能很好的理解 Spring IoC 的底层原理。
所以,就让我们一起来消灭掉这个熟悉又陌生的"敌人"吧!

版本约定

Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)

正文

先从类的继承关系图这个顶层视角来了解一下 BeanDefinition 吧!

RootBeanDefinition

RootBeanDefinition 本质上是 Spring BeanFactory 运行时统一的 BeanDefinition 视图。
它代表了一个 merged bean definition,是 Spring BeanFactory 在运行时支持的一个特殊的 bean。
自 Spring 2.5 以来,以编程方式注册 bean 定义的首选方法是 GenericBeanDefinition 类。GenericBeanDefinition 的优点是它允许动态定义父依赖项,而不是将角色“硬编码”为 RootBeanDefinition。

通过上面的解释,可以看出 RootBeanDefinition 的江湖地位可不小!它是 Spring BeanFactory 运行时统一的 BeanDefinition 视图。
在 Spring 源码中,我们也可以看到,Spring 在通过 BeanDefinition 创建 bean 的实例时,通常都会将 BeanDefinition 转化为 RootBeanDefinition 后,再进行 bean 实例的创建。

GenericBeanDefinition

GenericBeanDefinition 是以编程方式注册 BeanDefinition 的首选类。
它除了可以指定一些基本的 BeanDefinition 属性外,还可以通过“setParentName()”来灵活地配置 parent bean definition。
所以,GenericBeanDefinition 基本上可以完全替代掉 ChildBeanDefinition。

ChildBeanDefinition

它是一种可以继承 parent 配置的 BeanDefinition。
在创建 bean 时,会通过 AbstractBeanFactory#getMergedLocalBeanDefinition() 来将 child 和 parent bean definition 进行合并。
看一个例子:

StaticApplicationContext parent = new StaticApplicationContext();
StaticApplicationContext child = new StaticApplicationContext(parent);

RootBeanDefinition pbd = new RootBeanDefinition();
pbd.setBeanClass(FooService.class);
pbd.setScope(BeanDefinition.SCOPE_SINGLETON);
pbd.getPropertyValues().add("id", "123");
pbd.getPropertyValues().add("name", "zhangsan");
parent.registerBeanDefinition("fooService", pbd);

ChildBeanDefinition cbd = new ChildBeanDefinition("fooService");
cbd.setBeanClass(FooService.class);
cbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
cbd.getPropertyValues().add("id", "456");
child.registerBeanDefinition("fooService", cbd);

// 通过子容器来获取 bean
FooService bean1 = child.getBean(FooService.class);
FooService bean2 = child.getBean(FooService.class);
System.out.println(bean1 + ",id=" + bean1.getId() + ",name=" + bean1.getName());
System.out.println(bean2 + ",id=" + bean2.getId() + ",name=" + bean2.getName());

输出结果:
com.kvn.beans.child.FooService@146ba0ac,id=456,name=zhangsan
com.kvn.beans.child.FooService@4dfa3a9d,id=456,name=zhangsan

可以看出 getMergedLocalBeanDefinition() 时,会以 ChildBeanDefinition 的属性为准,同时,将 parent BeanDefinition 的属性合并进来。

merged bean definition

它不是一个具体的类,而是 Spring 对 BeanDefinition 进行的一种合并操作。

Spring 什么时候会对 BeanDefinition 进行 merge 操作呢?
答:在根据 BeanDefinition 创建 bean 的实例之前,会通过 AbstractBeanFactory#getMergedLocalBeanDefinition() 来进行 merge 操作。

BeanDefinition 的 merge 操作的作用是什么?
答:对 BeanDefinition 进行 merge 操作时,会将 child 的属性与 parent 的属性进行合并,当有相同属性时,以 child 的为准。
而类似 propertyValues、constructor args 这种集合类的参数属性的话,就会取并集。
所以,上面 ChildBeanDefinition 例举的例子中,ChildBeanDefinition 中并没有 name 这个属性,而通过子容器获取到的 FooService bean 中的 name 却有了值,并且值是 parent BeanDefinition 中的属性值。
这也证明了 child 与 parent 的 BeanDefinition 属性会进行合并。

小结

本文重点讲解了 RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition、merged bean definition
其中重点需要掌握的有:

  • RootBeanDefinition
    它是 Spring BeanFactory 运行时统一的 BeanDefinition 视图

  • GenericBeanDefinition
    它是以编程方式注册 BeanDefinition 的首选类

  • merged bean definition
    它不是一个具体的类,而是 Spring 对 BeanDefinition 进行的一种合并操作。
    merge 操作会将 child 的属性与 parent 的属性进行合并,当有相同属性时,以 child 的为准。

如果本文对你有所帮助,欢迎点赞收藏

有关 Spring 源码方面的问题欢迎一起交流,备注:51cto (vx: Kevin-Wang001)


博主好课推荐:

课程 地址
Dubbo源码解读——通向高手之路 https://edu.51cto.com/course/23382.html
正则表达式基础与提升 https://edu.51cto.com/course/16391.html

以上是关于#yyds干货盘点# Spring源码三千问BeanDefinition详解——什么是 RootBeanDefinition?merged bean definition 又是什么鬼?的主要内容,如果未能解决你的问题,请参考以下文章

#yyds干货盘点#Spring源码三千问@Lazy延迟加载与延迟注入有什么区别?

#yyds干货盘点# Spring源码三千问Bean的Scope有哪些?scope=request是什么原理?

#yyds干货盘点# Spring源码三千问BeanDefinition详解——什么是 RootBeanDefinition?merged bean definition 又是什么鬼?

#yyds干货盘点# Spring源码三千问Spring动态代理:什么时候使用的 cglib,什么时候使用的是 jdk proxy?

#yyds干货盘点# Spring 源码三千问同样是AOP代理bean,为什么@Async标记的bean循环依赖时会报错?

#yyds干货盘点# Spring源码三千问为什么要用三级缓存来解决循环依赖问题?二级缓存行不行?一级缓存行不行?