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

前言

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

版本约定

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

正文

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

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 操作。
mergeBeanDefinition

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 可以免费下载源码测试工程…

阅读更多文章,请关注公众号: 老王学源码
gzh


系列博文:
【老王读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源码三千问】为什么要用三级缓存来解决循环依赖问题?二级缓存行不行?一级缓存行不行?

  • 13
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老王学源码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值