Spring Bean生命周期之BeanDefinition的合并过程详解

作者:码农的进阶之路 时间:2023-11-29 02:50:35 

写在前面

注:本文章使用的 SpringBoot 版本为 2.2.4.RELEASE,其 Spring 版本为 5.2.3.RELEASE

前言

书接上文,BeanDefinition注册到IoC容器后,紧接着就是要使用Bean了,要使用必须先要获取Bean,这里我们就以DefaultListableBeanFactory#getBean方法来引出本次讨论的内容:BeanDefinition的合并

通过前面的章节我们了解到了BeanDefinition,那什么是BeanDefinition的合并呢?为什么要进行合并呢? 带着这个问题,我们到源码中去找找答案。

为了使源码逻辑有个参照,这里先给出一个案例,在分析源码时 将这个案例也代入进去方便我们理解源码

BeanDefinition的合并源码分析

实体类

@Data
public class SuperUser implements Serializable {
   private String address;
   public SuperUser(String address) {
       this.address = address;
   }
   public SuperUser() {
   }
}
@Data
@ToString(callSuper = true)
public class User extends SuperUser {
   private String name;
   private Integer age;
   public User() {
   }
   public User(String name, Integer age) {
       this.name = name;
       this.age = age;
   }
}

基于GenericBeanDefinition注册有层次的Bean

public class GenericBeanDefinitionDemo {
   public static void main(String[] args) {
       AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
       //父BeanDefinition
       GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
       rootBeanDefinition.setBeanClass(SuperUser.class);
       //设置参数
       MutablePropertyValues propertyValues = new MutablePropertyValues();
       propertyValues.addPropertyValue("address", "地址");
       rootBeanDefinition.setPropertyValues(propertyValues);
       //子BeanDefinition
       GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
       childBeanDefinition.setBeanClass(User.class);
       //设置构造参数
       ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
       argumentValues.addIndexedArgumentValue(0, "我就是我");
       argumentValues.addIndexedArgumentValue(1, 18);
       childBeanDefinition.setConstructorArgumentValues(argumentValues);
       childBeanDefinition.setParentName("superUser");
       //类型相同时 以子类为主
       childBeanDefinition.setPrimary(true);
       context.registerBeanDefinition("superUser", rootBeanDefinition);
       context.registerBeanDefinition("user", childBeanDefinition);
       context.refresh();
       User user = context.getBean("user", User.class);
       System.out.println(user);
       SuperUser superUser = context.getBean("superUser", SuperUser.class);
       System.out.println(superUser);
       context.close();
   }
}

在分析源码时我们要有侧重点,这里会将不太相关的逻辑一带而过。

AbstractBeanFactory#doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//将name解析为beanName,如果传入的是alias,根据aliasMap进行转换,我们在前面介绍过了
final String beanName = transformedBeanName(name);
Object bean;
// 如果是单例Bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//省略日志输出
// 这里的逻辑是根据beanName判断是否为FactoryBea,并采用相应方式去处理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
//如果不是单例对象
else {
// 对原型对象进行验证,如果当前beanName已经在创建中了 抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 获取父BeanFactory,前面我们介绍过了 BeanFactory允许有层级,可已存在父BeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
//如果存在父BeanFactory 去父BeanFactory中查找bean
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//省略去父BeanFactory查找Bean的过程
}
//typeCheckOnly默认为false ,这里将beanName放到alreadyCreated集合中 表示该Bean正在创建中
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
   // 这里来到了我们要重点关注的地方了,bd的合并 ⭐️
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
//如果存在依赖Bean,需要进行依赖查找
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// 省略dependsOn 依赖查找代码
}
// 这里的if..else if .. else 是根据scope取值来的
//scope=singleton时
if (mbd.isSingleton()) {
//省略单实例Bean创建过程
}
//scope=prototype时
else if (mbd.isPrototype()) {
//省略Prototype Bean创建过程
}
//scope=request、application、session时
else {
// 省略其他Scope Bean的创建过程
}
if (requiredType != null && !requiredType.isInstance(bean)) {
//省略类型转换代码
}
// 返回创建的Bean
return (T) bean;
}

上面的方法实现比较长、比较复杂,这里只对重要的地方进行些注释说明并将与本次讨论无关的代码先行进行注释。

下面就进入到BeanDefinition的合并逻辑了

//假设beanName=user
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// 检查缓存,若存在直接返回
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null) {
return mbd;
}
   //getBeanDefinition(beanName)==>实际上去DefaultListableBeanFactory.beanDefinitionMap中根据key查找BeanDefinition,这在注册阶段已经放到beanDefinitionMap了。
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
throws BeanDefinitionStoreException {
return getMergedBeanDefinition(beanName, bd, null);
}
//根据上面的举例可知beanName=user,bd是User类的BeanDefinition,containingBd=null
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
throws BeanDefinitionStoreException {
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
// 尝试从缓存中拿
if (containingBd == null) {
mbd = this.mergedBeanDefinitions.get(beanName);
}
if (mbd == null) {
       //如果当前BeanDefinition没有指定parentName,说明其不存在父BeanDefinition,不需要合并。以RootBeanDefinition形式展现
if (bd.getParentName() == null) {
// 如果bd是RootBeanDefinition类型,直接类型转换
if (bd instanceof RootBeanDefinition) {
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
           //通过bd属性构造RootBeanDefinition
mbd = new RootBeanDefinition(bd);
}
}
else {
// 走到这里说明存在parentName,当前bd需要与其父bd合并
BeanDefinition pbd;
try {
           //得到父BeanName
String parentBeanName = transformedBeanName(bd.getParentName());
           //!beanName.equals(parentBeanName) 条件成立 说明当前beanName属于子bd
if (!beanName.equals(parentBeanName)) {
             //递归地以父bd名称 查找父BeanDefinition。之所以递归地查找,是因为 可能此时的parentBeanName还有父,实体类存在多重继承关系
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
             //走到这里,说明beanName.equals(parentBeanName),很有可能是父bd查找BeanDefinition时走来的。
             //获取父BeanFactory,BeanFactory也是有层次的,有父子关系的,可参见ConfigurableBeanFactory#setParentBeanFactory
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);
}
// pbd是父BeanDefinition,由其构造为RootBeanDefinition
mbd = new RootBeanDefinition(pbd);
         //bd是子BeanDefinition,主要是继承父类的属性,并覆盖与父类同名的属性,有兴趣的可以看一下overrideFrom方法实现
mbd.overrideFrom(bd);
}
// 如果父bd未指定scope,则设置默认值
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
}
//由于containingBd=null 这里就不看了
if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
mbd.setScope(containingBd.getScope());
}
if (containingBd == null && isCacheBeanMetadata()) {
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
//最终返回根据当前beanName找到的bd
return mbd;
}
}

分析了上面的源码,我们试着总结一下:

1、如果不存在parentName,即不需要被合并,直接将bd转为RootBeanDefinition 返回即可

2、如果存在parentName

  • 先根据parentName 找到父bd,若实体存在多级继承关系,则需要递归地查找。

  • 将父bd转为RootBeanDefinition,并将子bd与父bd进行合并

  • 设置一些其他属性

来源:https://blog.csdn.net/zyxwvuuvwxyz/article/details/123228093

标签:Spring,Bean,生命周期,Definition,合并
0
投稿

猜你喜欢

  • c# 引用Nlog插件的步骤

    2021-08-10 01:03:43
  • Springcloud Nacos基本操作代码实例

    2023-07-07 18:24:22
  • 分享java中设置代理的两种方式

    2023-10-28 10:48:52
  • Android性能优化之利用强大的LeakCanary检测内存泄漏及解决办法

    2021-07-26 11:57:46
  • C++实现String类的方法详解

    2023-04-27 08:49:27
  • Android开发使用Handler的PostDelayed方法实现图片轮播功能

    2023-10-20 04:37:42
  • 深入理解Java并发编程之ThreadLocal

    2023-11-21 02:43:42
  • android中图片的三级缓存cache策略(内存/文件/网络)

    2023-09-04 19:51:59
  • 轻松学习C#的String类

    2021-06-21 02:03:06
  • Java上传文件图片到服务器的方法

    2022-12-06 01:40:46
  • Java压缩解压zip技术_动力节点Java学院整理

    2022-08-29 22:50:42
  • C#/Java连接sqlite与使用技巧

    2021-07-10 08:34:05
  • Android Jetpack库剖析之LiveData组件篇

    2022-08-31 02:08:13
  • Java使用Hutool实现AES、DES加密解密的方法

    2021-06-03 16:49:57
  • Android动画之小球拟合动画实例

    2023-06-11 00:47:46
  • Java实例讲解动态代理

    2023-03-21 22:56:45
  • C# pictureBox用法案例详解

    2022-02-24 19:40:07
  • Java对XML文件增删改查操作示例

    2021-10-28 08:46:29
  • mybatis打印SQL,并显示参数的实例

    2022-08-03 16:46:59
  • Flutter集成高德地图并添加自定义Maker的实践

    2022-07-11 21:32:49
  • asp之家 软件编程 m.aspxhome.com