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

目录
  • 前言
  • BeanDefinition的合并源码分析
  • 总结

写在前面

注:本文章使用的 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进行合并
  • 设置一些其他属性

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Spring中如何使用@Value注解实现给Bean属性赋值

    目录 属性赋值 @Value注解的定义: 测试 1.在添加了Spring依赖的Maven项目中创建 2.在resources目录下创建一个配置文件person.properties 3.创建配置类 4.创建测试类进行测试 5.测试结果: 如何给Bean的属性赋值(注入) 1.通过构造方法设置值. 2.设置注入(通过set方法) 属性赋值 只用Spring注解开发的时候,可以使用@Value搭配@PropertySource注解进行给Bean的属性进行赋值. @Value @Value注解的定义:

  • Spring Bean生命周期之属性赋值阶段详解

    目录 前言 属性自动装配 属性赋值前回调 属性的真正赋值 总结 前言 上节在谈论Bean的实例化过程时,在说明实例化后阶段时只是粗略地看了一下populateBean,并未展开分析.本节接着populateBean开始分析对象赋值阶段的事情. populateBean其实主要做了以下几件事: Bean实例化后回调,来决定是否进行属性赋值 (上节分析过了) 对属性进行自动装配 InstantiationAwareBeanPostProcessor属性赋值前回调 属性的真正赋值 protected

  • Spring Bean生命周期之Bean的注册详解

    目录 前言 BeanFactory的继承体系 Bean的注册 alias别名的注册 总结 前言 上篇文章介绍了Bean元信息的配置与解析过程,限于篇幅Bean注册过程就没展开. 这里主要围绕BeanDefinitionReaderUtils#registerBeanDefinition展开分析下Bean注册过程 public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinit

  • Spring Bean生命周期之Bean元信息的配置与解析阶段详解

    目录 BeanDefinitionReader体系 BeanDefinitionReader接口定义 元信息配置与解析方式 XmlBeanDefinitionReader元信息解析源码分析 AnnotatedBeanDefinitionReader元信息解析源码分析 总结 写在前面 注:本文章使用的 SpringBoot 版本为 2.2.4.RELEASE,其 Spring 版本为 5.2.3.RELEASE 虽然Bean的创建可以采用BeanDefinition API 也可以直接采用注解方式

  • Spring Bean生命周期之Bean的实例化详解

    目录 前言 实例化前阶段 实例化阶段 实例化后阶段 总结 前言 上一节说到了BeanDefinition的合并过程,这节该说Bean的实例化过程了.根据AbstractAutowireCapableBeanFactory#createBean源码逻辑 可将实例化过程分为实例化前阶段.实例化过程.实例化后阶段. 实例化前阶段 protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[]

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

    目录 前言 BeanDefinition的合并源码分析 总结 写在前面 注:本文章使用的 SpringBoot 版本为 2.2.4.RELEASE,其 Spring 版本为 5.2.3.RELEASE 前言 书接上文,BeanDefinition注册到IoC容器后,紧接着就是要使用Bean了,要使用必须先要获取Bean,这里我们就以DefaultListableBeanFactory#getBean方法来引出本次讨论的内容:BeanDefinition的合并 通过前面的章节我们了解到了BeanD

  • Spring Bean生命周期详细分析

    目录 前言 一.Bean的介绍 什么是Bean Bean的生命周期 Bean的作用域 二.详细过程 1. Bean的实例化 2. InstantiationAwareBeanPostProcessor 3. 设置属性(依赖注入) 4. 注入Aware接口 5. BeanPostProcessor的postProcessBeforeInitialzation方法 6. InitializingBean与init-method 7. BeanPostProcess的postProcessAfterI

  • 谈谈我对Spring Bean 生命周期的理解

    前言 Spring的ioc容器功能非常强大,负责Spring的Bean的创建和管理等功能.而Spring 的bean是整个Spring应用中很重要的一部分,了解Spring Bean的生命周期对我们了解整个spring框架会有很大的帮助. BeanFactory和ApplicationContext是Spring两种很重要的容器,前者提供了最基本的依赖注入的支持,而后者在继承前者的基础进行了功能的拓展,例如增加了事件传播,资源访问和国际化的消息访问等功能.本文主要介绍了ApplicationCo

  • 全面详解Spring Bean生命周期教程示例

    目录 Spring 中 Bean 的生命周期 Bean 的实例化 构造方法注入 工厂方法注入 Bean 的属性赋值 setter注入 构造方法注入 Bean 的初始化 初始化方法 InitializingBean 接口 Bean 的销毁 销毁方法 DisposableBean 接口 总结 Spring 中 Bean 的生命周期 是当今最流行的 Java 开发框架之一,其强大的 Bean容器机制是其中的核心之一.Bean 是指在 Spring 容器中被管理的对象,它们可以被注入到其他对象中,也可以

  • Vue生命周期与后端交互实现流程详解

    目录 表单控制 购物车案例 v-model进阶(了解) vue生命周期 与后端交互 电影案例 表单控制 1.input:checkbox(单选,多选),radio(单选) 2.代码展示 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="j

  • Spring框架实现AOP添加日志记录功能过程详解

    这篇文章主要介绍了Spring框架实现AOP添加日志记录功能过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 需求,在调用业务方法的时候,在被调用的业务方法的前面和后面添加上日志记录功能 整体架构: 日志处理类: package aop; import java.util.Arrays; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; //日志处理类 增

  • 浅谈Spring bean 生命周期验证

    一.从源码注释看bean生命周期 从JDK源码上看,BeanFactory实现类需要支持Bean的完整生命周期,完整的初始化方法及其标准顺序(格式:接口 方法)为: 1.BeanNameAware setBeanName 设置bean名称 2.BeanClassLoaderAware setBeanClassLoader 设置bean类加载器 3.BeanFactoryAware setBeanFactory 设置bean工厂 4.EnvironmentAware setEnvironment

  • Spring Bean生命周期源码原理图解

    概述 spring流行了很长时间了,是一个用java编写的轻量级框架,受到了很多公司及程序员的欢迎,Bean生命周期是一个对象从实例化开始到销毁的全过程,了解生命周期是很有必要的. 重要性 spring的生命周期是比较复杂的,只有了解其过程及原理才能更好的扩展程序. 源码剖析生命周期过程bean的实例化 属性填充及Aware接口检测设置依赖 如果容器自定义了BeanpostProcessor的实现类,则执行处理器相应的前置处理 bean实现了初始化接口InitlializingBean,则会执行

随机推荐