广告
返回顶部
首页 > 资讯 > 后端开发 > Python >SpringBean生命周期之Bean的注册详解
  • 542
分享到

SpringBean生命周期之Bean的注册详解

2024-04-02 19:04:59 542人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录前言BeanFactory的继承体系Bean的注册alias别名的注册总结前言 上篇文章介绍了Bean元信息的配置与解析过程,限于篇幅Bean注册过程就没展开。 这里主要围绕Be

前言

上篇文章介绍了Bean元信息的配置与解析过程,限于篇幅Bean注册过程就没展开。

这里主要围绕BeanDefinitionReaderUtils#reGISterBeanDefinition展开分析下Bean注册过程

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {
		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

上面无论是注册bd还是建立alias-beanName之间的关系,均用到了BeanDefinitionRegistry,因此我们就以它为突破口来展开

BeanFactory的继承体系

在这里插入图片描述

对图中常用接口或类进行说明:

  • ListableBeanFactory 集合类型BeanFactory 提供一种可以查找所有Bean实例的能力
    • getBeanNamesForType(Class) 根据类型去查找Bean名称列表不会强制Bean的初始化,可从源码中看出来
    • getBeansOfType(Class) 根据类型去查找Bean实例列表,会强制Bean的初始化,可从源码中看出来
    • getBeanNamesForAnnotation(Class) 根据注解类型获取Bean名称列表
    • getBeansWithAnnotation(Class) 根据注解类型获取Bean实例列表
    • findAnnotationOnBean(String,Class) 根据指定名称+标注类型获取Bean实例
  • Hierarchical([ˌhaɪəˈrɑːkɪkl])BeanFactory 层次性BeanFactory,有父子容器的概念,可在ConfigurableListableBeanFactory设置其父容器
    • getParentBeanFactory() 获取父容器
    • boolean containsLocalBean(String name) 在当前容器中查找是否存在该名称的Bean实例
  • SingletonBeanRegistry 单实例BeanFactory,与单实例有关
  • ConfigurableBeanFactory 可配置的BeanFactory,这个一般不用于应用程序,是给其他BeanFactory扩展用的。的确,定义了很多配置方法
  • ConfigurableListableBeanFactory 可配置的集合类型的BeanFactory
  • AutowireCapableBeanFactory 提供具有自动装配能力的BeanFactory

透过继承体系可以看出,BeanDefinitionRegistry的实现类是DefaultListableBeanFactory,该类同时实现了诸多接口,可谓是BeanFactory中集大成者,因此我们到DefaultListableBeanFactory中阅读下bd注册及别名注册的源码

Bean的注册

先来分析下DefaultListableBeanFactory的几个重要的成员属性

// 这个实质上就是ioc容器中Bean的载体,没错 它很重要,但它是无序的
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
//它代表了bd名称的集合,它是有序的 遵循bd注册的顺序
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
// 这是已创建bd名称的集合,在doGetBean方法根据beanName创建Bean时,beanName会被加到此集合中
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

上面两个属性都比较重要,两者结合使用的话可以实现bd的顺序访问(其实就是遍历beanDefinitionNames集合时,使用beanDefinitionMap去获取bd)

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
		//对beanName、bd进行非空验证
		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
		//如果bd是AbstractBeanDefinition类型,则对bd进行验证(一般情况下 我们场景的bd都是继承自AbstractBeanDefinition的)
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
			   //bd验证
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				//省略异常代码
			}
		}
		//从beanDefinitionMap根据beanName取bd
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		//如果beanName名称的bd已经存在
		if (existingDefinition != null) {
			//如果不允许Bean被重新注册 则抛出异常,这里默认值是true
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			//如果已被注册bd的角色值小于当前待注册bd的角色值 
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// 省略日志输出
			}
			//如果已注册的同名bd 与本次注册的bd不相同
			else if (!beanDefinition.equals(existingDefinition)) {
				//省略日志输出
			}
			else {
				//省略日志输出
			}
			//将beanName-bd键值对放入beanDefinitionMap集合
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			//流程走到这里 说明在beanDefinitionMap中不存在同名bd
			//条件成立 说明alreadyCreated不为空 即有bd已被创建
			if (hasBeanCreationStarted()) {
				// 如果在此之间 有bean正在被创建 则这里进行加处理
				synchronized (this.beanDefinitionMap) {
				    //将beanName-bd键值对放入beanDefinitionMap集合
					this.beanDefinitionMap.put(beanName, beanDefinition);
					//将beanName添加到beanDefinitionNames集合中
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					//如果beanName是手动注册的单例Bean名称,则更新manualSingletonNames
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						//这里从集合中删除的原因个人理解:
						//manualSingletonNames记录的是在registerSingleton时被添加的单实例beanName,而这里注入的不是单实例Bean。因为manualSingletonNames包含了此beanName,因此需要剔除
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				//如果没有bean在被创建
				//将beanName-bd键值对放入beanDefinitionMap集合
				this.beanDefinitionMap.put(beanName, beanDefinition);
				//将beanName添加到集合中
				this.beanDefinitionNames.add(beanName);
				//这里从manualSingletonNames中剔除,个人理解为拖地操作,毕竟若集合中没有此beanName 也remove不了
				this.manualSingletonNames.remove(beanName);
			}
			//这个集合表示冻结配置时缓存的beanName集合,暂时未理解透此集合的用途
			this.frozenBeanDefinitionNames = null;
		}
		//如果已存在同名bd或已存在同名的单例对象,则重置所有已被缓存的同名的bd数据,因此这里bd注册成功后,肯定后续还会再生成Bean的
		if (existingDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}

其实分析下来发现Bean注册的过程还是比较容易理解的,下面试着总结一下:

  • 若bd未被注册过,则将bd信息存入BeanDefinitionMap等集合中
  • 若bd已被注册过,允许覆盖注册的情况下,将bd信息存入BeanDefinitionMap等集合中,并清除已被缓存的同名bd信息

下面看一下清除bd信息的代码逻辑

protected void resetBeanDefinition(String beanName) {
		// 如果此bd属于被合并的BeanDefinition,则这里将其从MergeBeanDefinition集合中剔除
		clearMergedBeanDefinition(beanName);
		// 如果已存在同名的单例对象 则销毁,具体细节先不展开
		destroySingleton(beanName);
		// 这里for循环逻辑与MergeBeanDefinition相关,如果存在MergedBeanDefinitionPostProcessor,则重置此bd
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			if (processor instanceof MergedBeanDefinitionPostProcessor) {
				((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName);
			}
		}
		// BeanDefinition运行有层级的,如果此bd拥有多个父级bd,那么这里递归地重置其父bd
		for (String bdName : this.beanDefinitionNames) {
			if (!beanName.equals(bdName)) {
				BeanDefinition bd = this.beanDefinitionMap.get(bdName);
				if (beanName.equals(bd.getParentName())) {
					resetBeanDefinition(bdName);
				}
			}
		}
	}

alias别名的注册

看了Bean的注册,再来看别名的注册 发现流程比较清晰,基本上一目了然。

//注意 这里的name 不要具象为beanName,虽然我们是从建立beanName--alias关系出发追溯到这里的
public void registerAlias(String name, String alias) {
		//对name、alias进行断言验证
		Assert.hasText(name, "'name' must not be empty");
		Assert.hasText(alias, "'alias' must not be empty");
		synchronized (this.aliasMap) {
			//如果别名与beanName相同,那别名就没有必要存在了,因此选择直接从this.aliasMap中移除此别名
			if (alias.equals(name)) {
				this.aliasMap.remove(alias);
				//省略日志输出
			}
			else {
			   //从aliasMap中根据别名获取name
				String registeredName = this.aliasMap.get(alias);
				if (registeredName != null) {
				    //如果已存在的registeredName与此此要注册的name一致,那就没必要注册了
					if (registeredName.equals(name)) {
						return;
					}
					//流程走到这里,说明同一个别名,对应两个name,如果不允许alias覆盖 则抛出异常
					if (!allowAliasOverriding()) {
						//省略异常及日志输出
				}
				//这里对alias进行循环检查,避免出现A的别名是B,B的别名是A的情况
				checkForAliasCircle(name, alias);
				//将alias--name 放入aliasMap
				this.aliasMap.put(alias, name);
				//省略日志输出
			}
		}
	}

alias与beanName的映射关系,为根据名称查找Bean又提供了一种思路。就是说除了根据beanName外,也可以根据alias去查找Bean。

这部分源码如下

//name可以是beanName,也可以是alias
public String canonicalName(String name) {
		//局部变量赋值
		String canonicalName = name;
		// Handle aliasing...
		String resolvedName;
		do {
		    //如果从aliasMap中能根据alias分析出beanName
			resolvedName = this.aliasMap.get(canonicalName);
			if (resolvedName != null) {
				canonicalName = resolvedName;
			}
		}
		while (resolvedName != null);
		// 无论入参name是beanName还是alias,这里返回的都应该是beanName了
		return canonicalName;
	}

总结

好了,这篇主要分析了BeanDefinition的注册,顺带着也说了别名的注册情况。既然BeanDefinition已经注册完成,那紧接着就是BeanDefinition的实例化过程了,这个放到下次分析吧。

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

--结束END--

本文标题: SpringBean生命周期之Bean的注册详解

本文链接: https://www.lsjlt.com/news/141331.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • SpringBean生命周期之Bean的注册详解
    目录前言BeanFactory的继承体系Bean的注册alias别名的注册总结前言 上篇文章介绍了Bean元信息的配置与解析过程,限于篇幅Bean注册过程就没展开。 这里主要围绕Be...
    99+
    2022-11-13
  • SpringBean生命周期之Bean的实例化详解
    目录前言实例化前阶段实例化阶段实例化后阶段总结前言 上一节说到了BeanDefinition的合并过程,这节该说Bean的实例化过程了。根据AbstractAutowireCapab...
    99+
    2022-11-13
  • spring之Bean的生命周期详解
    Bean的生命周期:Bean的定义——Bean的初始化——Bean的使用——Bean的销毁Bean的定义Bean 是 spring 装配的组件模型,一切实体类都可以配置成一个 Bean ,进而就可以在任何其他的 Bean 中使用,一个 Be...
    99+
    2023-05-31
    spring bean 生命周期
  • SpringBean生命周期之Bean元信息的配置与解析阶段详解
    目录BeanDefinitionReader体系BeanDefinitionReader接口定义元信息配置与解析方式XmlBeanDefinitionReader元信息解析 源码分析...
    99+
    2022-11-13
  • SpringBean生命周期之BeanDefinition的合并过程详解
    目录前言BeanDefinition的合并源码分析总结写在前面 注:本文章使用的 SpringBoot 版本为 2.2.4.RELEASE,其 Spring 版本为 5.2.3.RE...
    99+
    2022-11-13
  • Java之SpringBean生命周期问题理解
    Spring Bean的生命周期? 首先说一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy; Spring上下文中的Bean生命周期也类...
    99+
    2022-11-12
  • SpringBean生命周期之属性赋值阶段详解
    目录前言属性自动装配属性赋值前回调属性的真正赋值总结前言 上节在谈论Bean的实例化过程时,在说明实例化后阶段时只是粗略地看了一下populateBean,并未展开分析。本节接着po...
    99+
    2022-11-13
  • Spring 中 Bean 的生命周期详解
    目录前言1.Bean 生命周期2.代码演示总结前言 Java 中的公共类称之为 Bean 或 Java Bean,而 Spring 中的 Bean 指的是将对象的生命周期,交个 Sp...
    99+
    2022-11-13
  • Java开发学习之Bean的生命周期详解
    目录一、什么是生命周期二、环境准备三、生命周期设置步骤1:添加初始化和销毁方法步骤2:配置生命周期步骤3:运行程序四、close关闭容器五、注册钩子关闭容器六、bean生命周期总结一...
    99+
    2022-11-13
  • Spring源码解析之Bean的生命周期
    一、Bean的实例化概述 前一篇分析了BeanDefinition的封装过程,最终将beanName与BeanDefinition以一对一映射关系放到beanDefinitionMa...
    99+
    2022-11-12
  • SpringBoot源码之Bean的生命周期
    入口方法为SpringApplication#run() 1.SpringApplication#run() public ConfigurableApplicationCont...
    99+
    2023-05-15
    SpringBoot之Bean的生命周期 bean生命周期 SpringBean生命周期
  • Java Bean的作用域,生命周期和注解
    目录Bean的作用域singleton作用域Bean的生命周期1.创建Bean的实现类2.配置Bean 3.测试生命周期 Bean的装配方式基于XML配置的装配基于注解的装配1.@C...
    99+
    2022-11-12
  • Java之SpringBean生命周期问题的示例分析
    小编给大家分享一下Java之SpringBean生命周期问题的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Spring Bean的生命周期?首先说一下S...
    99+
    2023-06-20
  • Spring创建Bean的生命周期详析
    目录1.Bean 的创建生命周期2.Spring AOP 大致流程3.Spring 事务4.Spring 源码阅读前戏BeanDefinitionBeanDefinitionRead...
    99+
    2022-11-13
  • Java之Bean的生命周期实例分析
    本篇内容主要讲解“Java之Bean的生命周期实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java之Bean的生命周期实例分析”吧!一、什么是生命周期首先理解下什么是生命周期从创建到消...
    99+
    2023-07-02
  • Spring中bean的生命周期之getSingleton方法
    Spring中bean的生命周期 要想讲清楚spring中bean的生命周期,真的是不容易,以AnnotationConfigApplicationContext上下文为基础来讲解b...
    99+
    2022-11-12
  • 详解Spring中Bean的作用域与生命周期
    目录一、Bean的作用域二、Bean的生命周期使用代码演示Bean的生命周期一、Bean的作用域 通过Spring容器创建一个Bean的实例时,不仅可以完成Bean的实例化,还可以使...
    99+
    2022-11-12
  • Spring中Bean的作用域与生命周期详解
    目录一、Bean的作用域1、单实例Bean声明2、多实例Bean声明二、Bean的生命周期1、bean的初始和销毁2、bean的后置处理器总结一、Bean的作用域 首先我们来讲一下有...
    99+
    2022-11-12
  • Java开发学习之Bean的作用域和生命周期详解
    目录一、Bean 的作用域二、Spring 的执行流程三、Bean 的生命周期一、Bean 的作用域 在之前学习Java基础的时候,有接触到作用域这样的概念。一个变量并不一定在任何区...
    99+
    2022-11-13
  • 深入了解Spring的Bean生命周期
    目录源码下载什么是 Spring Bean 的生命周期Bean的生命周期Spring角度查看bean的定义与注册SpringBoot角度查看bean定义和注册实例化,依赖注入,初始化...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作