【Spring源码三千问】BeanDefinition详解——什么是 RootBeanDefinition?merged bean definition 又是什么鬼?

BeanDefinition详细分析

  • 前言
  • 版本约定
  • 正文
    • RootBeanDefinition
    • GenericBeanDefinition
    • ChildBeanDefinition
    • merged bean definition
  • 小结
  • SpringIoC源码视频讲解:

前言

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

版本约定

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

正文

先从类的继承关系图这个顶层视角来了解一下 BeanDefinition 吧!
【Spring源码三千问】BeanDefinition详解——什么是 RootBeanDefinition?merged bean definition 又是什么鬼?_第1张图片

RootBeanDefinition

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

RootBeanDefinition 的英文注释:
A root bean definition represents the merged bean definition that backs a specific bean in a Spring BeanFactory at runtime.
It might have been created from multiple original bean definitions that inherit from each other, typically registered as GenericBeanDefinitions.
A root bean definition is essentially the ‘unified’ bean definition view at runtime.

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

BeanDefinition 接口的实现类有很多,通过不同方式注册到 BeanDefinitionRegistry 中的 BeanDefinition 的类型可能都不太相同。
最终,在通过 BeanDefinition 来创建 bean 的实例时,通常都会调用 getMergedBeanDefinition 来获取到一个 RootBeanDefinition。
所以,RootBeanDefinition 本质上是 Spring 运行时统一的 BeanDefinition 视图。
@see: AbstractBeanFactory#getMergedLocalBeanDefinition()

GenericBeanDefinition

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

编程的方式创建 bean 可以使用 BeanDefinitionBuilder 来很方便的完成。

BeanDefinition bd = BeanDefinitionBuilder.childBeanDefinition("fooService")
                .setScope(BeanDefinition.SCOPE_PROTOTYPE)
                .addPropertyValue("id", "456")
                .getBeanDefinition();

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 的属性合并进来。

Spring 2.5 之后,推荐通过 GenericBeanDefinition#setParentName() 的方式来替代 ChildBeanDefinition。

merged bean definition

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

Spring 什么时候会对 BeanDefinition 进行 merge 操作呢?
答:在根据 BeanDefinition 创建 bean 的实例之前,会通过 AbstractBeanFactory#getMergedLocalBeanDefinition() 来进行 merge 操作。
【Spring源码三千问】BeanDefinition详解——什么是 RootBeanDefinition?merged bean definition 又是什么鬼?_第2张图片

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 的为准。


SpringIoC源码视频讲解:

课程 地址
SpringIoC源码解读由浅入深 https://edu.51cto.com/sd/68e86

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

源码测试工程下载:
老王读Spring IoC源码分析&测试代码下载
老王读Spring AOP源码分析&测试代码下载

公众号后台回复:下载IoC 或者 下载AOP 可以免费下载源码测试工程…

阅读更多文章,请关注公众号: 老王学源码
【Spring源码三千问】BeanDefinition详解——什么是 RootBeanDefinition?merged bean definition 又是什么鬼?_第3张图片


系列博文:
【老王读Spring IoC-0】Spring IoC 引入
【老王读Spring IoC-1】IoC 之控制反转引入
【老王读Spring IoC-2】IoC 之 BeanDefinition 扫描注册
【老王读Spring IoC-3】Spring bean 的创建过程
【老王读Spring IoC-4】IoC 之依赖注入原理
【老王读Spring IoC-5】Spring IoC 小结——控制反转、依赖注入

相关阅读:
【Spring源码三千问】@Resource 与 @Autowired 的区别
【Spring源码三千问】bean name 的生成规则
【Spring源码三千问】BeanDefinition详细分析
【Spring源码三千问】Spring 是怎样解决循环依赖问题的?
【Spring源码三千问】哪些循环依赖问题Spring解决不了?
【Spring源码三千问】@Lazy为什么可以解决特殊的循环依赖问题?
【Spring源码三千问】BeanDefinition注册、Bean注册、Dependency注册有什么区别?
【Spring源码三千问】Bean的Scope有哪些?scope=request是什么原理?
【Spring源码三千问】为什么要用三级缓存来解决循环依赖问题?二级缓存行不行?一级缓存行不行?

你可能感兴趣的:(#,Spring,源码三千问,spring,ioc)