浅谈BeanDefinitionBeanDefinitionMapRootBeanDefintion三者的关系
Posted 默辨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈BeanDefinitionBeanDefinitionMapRootBeanDefintion三者的关系相关的知识,希望对你有一定的参考价值。
文章目录
通过本文你将收获:
-
RootBeanDefintion、BeanDefinition、BeanDefinitionMap三者的关系即其基本含义
-
BeanDefinition接口的实现子类
-
BeanDefinition子类中beanClass成员变量的含义
-
根据beanName获取RootBeanDefintion的流程
一、BeanDefinition
BeanDefinition:Bean定义的接口,在Spring中该接口有许多实现类如下图。不同的BeanDefinition的实现类也代表着不同的含义。
1、具体实现子类
- 如果使用配置类的方式启动Spring容器,那么我们就会生成AnnotatedGenericBeanDefinition
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
-
在实例化Bean之前,Spring需要先去加载自己的一些内部的Bean,比如@Configuration对应的Bean,此时使用的是RootBeanDefinition
-
当我们通过包路径去扫描Bean时,扫描出来的Bean定义使用的是ScannedGenericBeanDefinition
观察上面的三种情况:
- 将配置类初始化为一个BeanDefinition的实现类
- 将@Configuration注解对应的类初始化为一个BeanDefinition的实现类
- 将扫描出来的符合要求Bean要求的类初始化为一个BeanDefinition的实现类
2、手动创建子类
同理,我们是不是也能添加BeanDefinition到Spring容器中?
于是你就可以看到如下的几步代码,该代码表示我们手动创建一个BeanDefinition的实现类,并将他添加到Spring容器中:
- 创建一个BeanDefinition
- 设置BeanDefinition的类型
- 将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);
// 构建父子Bean
beanDefinition2.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是否存在父BD
if (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的内容覆盖给子BD
mbd = 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时,数据是会覆盖的,自然就有问题)
以上是关于浅谈BeanDefinitionBeanDefinitionMapRootBeanDefintion三者的关系的主要内容,如果未能解决你的问题,请参考以下文章