Spring源码十七:Bean实例化入口探索

上一篇Spring源码十六:Bean名称转化我们讨论doGetBean的第一个方法transformedBeanName方法,了解Spring是如何处理特殊的beanName(带&符号前缀)与Spring的别名机制。今天我们继续往方法下面看:


doGetBean 

 这个方法比较长,之所以每次都放到这里也是方便大家回忆,一次两次肯定记不住的,不过具体到某个章节讨论的点,在代码后面的截图都会给大家标注出来的。

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {// 获取转换后的Bean名称final String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.// 检查单例缓存中是否有手动注册的单例Bean// 这里是三级缓存处理方法入Object sharedInstance = getSingleton(beanName);// 如果单缓存中存在则直接从缓存中获取,// 如果不存在就走else分支,进行实例化if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}// 获取Bean的实例对象bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// 如果当前bean还未被实例化,则在这个判断中准备实例化// Fail if we're already creating this bean instance:// We're assumably within a circular reference.// 如果bean的类型是prototype且正在创建,直接抛出异常if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.// 检查Bean定义是否存在于当前工厂/// 获取容器的父容器BeanFactory parentBeanFactory = getParentBeanFactory();// 存在父容器,且当前容器没有beanName的BeanDefinition,则通过父容器获取beanif (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else if (requiredType != null) {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}else {return (T) parentBeanFactory.getBean(nameToLookup);}}if (!typeCheckOnly) {// 标记bean已经开始创建,其实就是将当前beanName 放入alreadyCreated容器中markBeanAsCreated(beanName);}// 进入实例化bean阶段// 1、处理BeanDefinition:合并同名的BeanDefinition、是否达到实例化标准// 2、处理依赖的Bean,如果存在依赖其他的bean则递归所有需要依赖的bean,提前实例化需要依赖的Bean// 3、开始创建Bean实例对象:根据作用域分类处理:// 		3.1 单例bean创建:创建单例bean的实例对象注册到容器中、获取容器中的对象//		3.2.原型bean创建:实例化前置准备工作、创建原型bean实例对象注册都容器中、原型bean实例化后置处理、获取容器中的对象// 		3.3 其他bean创建:实例化前置准备工作、创建其他类型bean实例对象注册都容器中、其他类型bean实例化后置处理、获取容器中的对象// 4、返回响应处理,根据入参转化bean的类型try {// 合并容器中beanName对应的BeanDefinition,得到新的root类型BeanDefinitionfinal RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// 校验合并后的rootBeanDefinition是否达到实例化标准,abstractFlag默认是false,如果是true则抛出异常checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.// 获取当前beanName所依赖的BeanNames数组String[] dependsOn = mbd.getDependsOn();// 如果依赖的bean不为空,则注册这些bean且递归调用getBean,提前实例化需要依赖的Beanif (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 注册依赖的beanregisterDependentBean(dep, beanName);try {// 递归调用getBean,需要依赖的Bean先实例化getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// Create bean instance.// 判断beanDefinition是单例?if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {//是单例:则开始创建单例bean的实例return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});// 获取bean实例对象bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 如果是原型beanelse if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {// 实例化前置准备工作beforePrototypeCreation(beanName);// 开始实例化prototypeInstance = createBean(beanName, mbd, args);}finally {// 实例化以后的处理工作afterPrototypeCreation(beanName);}// 获取原型bean实例对象bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}// 其他bean类型处理else {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);// 空抛出异常if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {// 处理流程类似原型Object scopedInstance = scope.get(beanName, () -> {// 创建其他类型作用域bean的前置准备工作beforePrototypeCreation(beanName);try {// 创建其他类型作用域beanreturn createBean(beanName, mbd, args);}finally {// 后置处理作用afterPrototypeCreation(beanName);}});// 获取其他作用域类型实例化对象bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// Check if required type matches the type of the actual bean instance.// 如果requiredType,且bean类型与入参要求不一致,则需要惊喜转换if (requiredType != null && !requiredType.isInstance(bean)) {try {// 将BeanDefinition转化成指定类型的beanT convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}return convertedBean;}catch (TypeMismatchException ex) {if (logger.isTraceEnabled()) {logger.trace("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}

寻找实例化bean的入口

原型Bean校验

Spring中默认都是懒加载的单例bean,有因为单例bean全局唯一的特性,Spring为了提升性能避免频繁创建所以引入的缓存。将实例化过的bean放入缓存中。如下getSingleton方法就是判断单例缓存中是否存在同名的bean如果存在,则if逻辑。因为我们是第一次创建,缓存中自然没有我们的bean,所以进入else逻辑。

进入else逻辑,第一个处理方法如下,看到方法名称与注释大概也能这个方法是做了个基础校验,如果Bean的作用域是原型类型,且此时bean正在创建则抛出异常,说明

进入方法内部看下:

	protected boolean isPrototypeCurrentlyInCreation(String beanName) {Object curVal = this.prototypesCurrentlyInCreation.get();return (curVal != null &&(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));}

方法isPrototypeCurrentlyInCreation中有一个集合prototypesCurrentlyInCreation,主要的逻辑就是判断下集合中是否存在beanName,prototype类型的bean实例每次来获取时都会创建一个新的bean实例,所以Spring也就没必要为这种类型的bean进行缓存;没有存放的缓存所以,一旦发现名称为beanName的bean正在创建,就会抛出异常来终止本次bean的创建。

当前容器没有beanName的BeanDefinition尝试去父容器获取

接着往下看:

有上述代码可知道:如果当前的Spring容器的父类容器是存在的,并且当前Spring容器中不存在beanName对应的BeanDefinition,就会到Spring的父类容器中获取bean的实例了。

进入originalBeanName方法看下:

	protected String originalBeanName(String name) {String beanName = transformedBeanName(name);if (name.startsWith(FACTORY_BEAN_PREFIX)) {beanName = FACTORY_BEAN_PREFIX + beanName;}return beanName;}

这个方法要注意下,他对name进行了处理,先通过transformedBeanName方法获取转换后的名称,然后在判断原始参数传入的name是否以&开头,如果是则在拼上去,至于为啥怎么做后面在看,这里心眼有个印象:知道主要是处理FactoryBean好。

咱们再接着往下看:

上述代码就比较简单,无非是根据入参情况,去父类容器获取bean。

可以看到默认传入的都是false,这里还是简单看下markBeanAsCreate方法:

	protected void markBeanAsCreated(String beanName) {if (!this.alreadyCreated.contains(beanName)) {synchronized (this.mergedBeanDefinitions) {if (!this.alreadyCreated.contains(beanName)) {// Let the bean definition get re-merged now that we're actually creating// the bean... just in case some of its metadata changed in the meantime.clearMergedBeanDefinition(beanName);this.alreadyCreated.add(beanName);}}}}

上述方法判断集合alreadyCreated中是否存在beanName,alreadyCreated是用来记录已经创建了的bean的名称。

上述方法有一个double check不知道小伙伴有没有注意到,这个在框架内部还是有很多地方有使用到的。作用嘛,当然这也是为了避免多线程并发的问题,方法中最为关键的,就是在集合alreadyCreated中添加beanName,并且清除了beanName对应BeanDefinition相关的信息。

可以发现到目前为止都是准备工作,真正的入口就在下面:咱们我们接着往下看

 Bean实例步骤

// 进入实例化bean阶段// 1、处理BeanDefinition:合并同名的BeanDefinition、是否达到实例化标准// 2、处理依赖的Bean,如果存在依赖其他的bean则递归所有需要依赖的bean,提前实例化需要依赖的Bean// 3、开始创建Bean实例对象:根据作用域分类处理:// 		3.1 单例bean创建:创建单例bean的实例对象注册到容器中、获取容器中的对象//		3.2.原型bean创建:实例化前置准备工作、创建原型bean实例对象注册都容器中、原型bean实例化后置处理、获取容器中的对象// 		3.3 其他bean创建:实例化前置准备工作、创建其他类型bean实例对象注册都容器中、其他类型bean实例化后置处理、获取容器中的对象// 4、返回响应处理,根据入参转化bean的类型try {// 合并容器中beanName对应的BeanDefinition,得到新的root类型BeanDefinitionfinal RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// 校验合并后的rootBeanDefinition是否达到实例化标准,abstractFlag默认是false,如果是true则抛出异常checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.// 获取当前beanName所依赖的BeanNames数组String[] dependsOn = mbd.getDependsOn();// 如果依赖的bean不为空,则注册这些bean且递归调用getBean,提前实例化需要依赖的Beanif (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 注册依赖的beanregisterDependentBean(dep, beanName);try {// 递归调用getBean,需要依赖的Bean先实例化getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// Create bean instance.// 判断beanDefinition是单例?if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {//是单例:则开始创建单例bean的实例return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});// 获取bean实例对象bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 如果是原型beanelse if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {// 实例化前置准备工作beforePrototypeCreation(beanName);// 开始实例化prototypeInstance = createBean(beanName, mbd, args);}finally {// 实例化以后的处理工作afterPrototypeCreation(beanName);}// 获取原型bean实例对象bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}// 其他bean类型处理else {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);// 空抛出异常if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {// 处理流程类似原型Object scopedInstance = scope.get(beanName, () -> {// 创建其他类型作用域bean的前置准备工作beforePrototypeCreation(beanName);try {// 创建其他类型作用域beanreturn createBean(beanName, mbd, args);}finally {// 后置处理作用afterPrototypeCreation(beanName);}});// 获取其他作用域类型实例化对象bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}

在上述代码中备注已经把try代码块的处理逻辑给大家分析了,这里简单总结下:

  1. 合并BeanDefinition:首先,Spring容器会合并同名的BeanDefinition,确保每个Bean的配置是最新的,并且检查BeanDefinition是否满足实例化的条件。

  2. 检查依赖关系:如果Bean定义了依赖关系(通过depends-on属性),Spring容器会先注册并实例化这些依赖的Bean。如果检测到循环依赖,会抛出异常。

  3. 创建Bean实例:根据Bean的作用域(单例、原型或其他自定义作用域),Spring容器会采取不同的实例化策略:

    • 单例Bean:如果Bean是单例的,Spring容器会创建一个实例并将其注册到单例缓存中。
    • 原型Bean:如果Bean是原型的,每次请求都会创建一个新的实例。
    • 其他作用域Bean:对于其他自定义作用域的Bean,Spring容器会根据作用域的定义来创建和管理Bean的生命周期。
  4. 处理Bean的创建异常:如果在创建过程中发生异常,Spring容器会进行清理工作,并抛出异常。

  5. 获取Bean实例:最后,根据需要,Spring容器会返回Bean的实例或其代理。

小结

本篇咱们通过doGetBean方法一步一步进入,先是跳过了三级缓存获取单例bean方法,然后着重看了了原型Bean创建前的校验、父容器创建bean的场景,以及Spring容器在创建Bean实例时所遵循的步骤和逻辑。下一篇我们将探索Spring是如何来实例化bean。

总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1474752.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

CDNOW_master.txt数据分析实战

一、数据详情 该数据集是常见的销售数据集&#xff0c;数据展示的是美国1997后的商品销售数据。包含四个字段&#xff0c;分别是用户id,购买时间&#xff0c;销售量&#xff0c;与销售金额。 二、数据读取与数据清洗 导入必要的包 \s代表的许多空格作为分割&#xff0c;names重…

实现antd designable平台的组件拖拽功能

平台&#xff1a;designable设计器 github&#xff1a;designable 目录 1 背景2 技术栈3 组件拖拽和放置3.1 类型定义3.2 拖拽3.3 放置 1 背景 由于业务需求&#xff0c;我们需要实现designable平台的一个简易版的组件拖拽功能。 #mermaid-svg-QrxSDGe9YyGG3LbQ {font-family:…

CSS学习(三大特性 盒子模型)

目录 Emmet语法 1.快速生成HTML结构语法 2.快速生成CSS样式语法 CSS的复合选择器 后代选择器 子选择器 并集选择器 伪类选择器 链接伪类选择器 focus伪类选择器 CSS的三大特性 层叠性 继承性 优先级 CSS盒子模型 组成 边框 边框 内边距 外边距 块级盒子水…

GESP C++一级真题

PDF图片1-7 点赞❤️关注&#x1f60d;收藏⭐️ 互粉必回&#x1f64f;&#x1f64f;&#x1f64f;

用ThreadLocal解决线程隔离问题

存在的以下代码所示的线程隔离问题&#xff1a; package study.用ThreadLocal解决线程隔离问题;/*线程隔离 - 在多线程并发场景下&#xff0c;每个线程的变量都应该是相互独立的线程A&#xff1a;设置&#xff08;变量1&#xff09; 获取&#xff08;变量1&#xff09;线程B&a…

Agilent 安捷伦 DSO91304A 高性能示波器

Agilent 安捷伦 DSO91304A 高性能示波器 DSO91304A Infiniium 高性能示波器&#xff1a;13 GHz 13 GHz4个模拟通道高达 1 Gpts 存储器和 40 GSa/s 采样率可以提供更完整的信号迹线捕获更低的本底噪声&#xff08;50 mV/格时为 1.73 mVrms&#xff09;和深入的抖动分析功能能够…

我国网络安全领域有哪些法律法规?主要内容是什么?

1. 背景介绍 网络信息安全方面的法规在全球范围内都有相应的立法&#xff0c;我们主要的立法有《网络安全法》、《密码法》、《数据安全法》以及《个人信息保护法》。当前也有一些相关的条例和管理办法&#xff0c;接下来就为大家一一介绍。 2. 法规介绍 在中国&#xff0c;…

Error in onLoad hook: “SyntaxError: Unexpected token u in JSON at position 0“

1.接收页面报错 Error in onLoad hook: "SyntaxError: Unexpected token u in JSON at position 0" Unexpected token u in JSON at position 0 at JSON.parse (<anonymous>) 2.发送页面 &#xff0c;JSON.stringify(item) &#xff0c;将对象转换为 JSO…

《昇思25天学习打卡营第14天|onereal》

第14天学习内容如下&#xff1a; Diffusion扩散模型 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 本教程在Jupyter Notebook上成功运行。如您下载本文档为Python文件&#xff0c…

跨越界限的温柔坚守

跨越界限的温柔坚守 —— 郑乃馨与男友的甜蜜抉择在这个光怪陆离、瞬息万变的娱乐圈里&#xff0c;每一段恋情像是夜空中划过的流星&#xff0c;璀璨短暂。然而&#xff0c;当“郑乃馨与男友甜蜜约会”的消息再次跃入公众视野&#xff0c;它不仅仅是一段简单的爱情故事&#xf…

PageHelper分页查询遇到的小问题

如果我们是这样子直接查询 pagehelper会拼接导我们的sql语句之后 这样子我们搜索出来的list&#xff0c;就是里面参杂了PageHelper的东西 所以我们可以直接转成我们的Page类型 但是如果我们搜索出来的是List<Blog>&#xff0c;我有些信息不想返回给前端&#xff0c;所以…

【游戏客户端】大话版本slg玩法正式上线~~

【游戏客户端】制作率土之滨Like玩法 大家好&#xff0c;我是Lampard家杰~~ 好久好久没有更新博客了&#xff0c;有不少大佬都在后台私信我催更&#xff0c;但是很悲伤这段时间都忙的不行QAQ 那在忙什么呢&#xff1f;就是在制作一个SLG类的玩法【帮派纷争】啦 &#xff0c;布…

IT高手修炼手册(4)PowerShell命令

一、前言 PowerShell是一个功能强大的命令行界面和脚本环境&#xff0c;它允许用户管理Windows操作系统和应用程序。 二、文件和目录操作 Get-ChildItem&#xff1a;列出指定路径下的文件和文件夹。简写为ls或dir。 Copy-Item&#xff1a;复制文件和文件夹。简写为copy或cp。 M…

2024暑假集训

Day1——枚举 Day2——测试 Day3——贪心 Day4、5——测试 ——————————————————————————————————————————— Day3T7&Day5T7:没思路 Day3T8:不知道怎么排序筛选 Day5T5:没有算法难度&#xff0c;但是不知道怎么处理2队奶牛的情…

moviepy给视频添加字幕很慢的问题解决

前面说了如何通过moviepy给视频添加字幕&#xff1a; https://blog.csdn.net/qq_30594137/article/details/140094118?spm1001.2014.3001.5502 但是真实添加字幕的时候&#xff0c;就会很耗时&#xff0c;这是因为如下问题导致的。 CompositeVideoClip的入参是个list&#x…

ComfyUI+MuseV+MuseTalk图片数字人

电脑配置 GPU12G&#xff0c;如果自己电脑配置不够&#xff0c;选择云gpu&#xff0c;我就是用的这个&#xff0c;自己电脑太老配置跟不上 环境&#xff1a; Python 3.11.8 torch 2.2.1 cuda_12.1 资源提供&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1_idZbF…

基于单片机的 LED 花样照明时钟设计

摘要 &#xff1a; 本设计是基于单片机的 LED 花样照明 &#xff0c; 并附加时钟设计 . 单片机也叫微控制器 &#xff08; Micro Control Unit, 简称 MCU &#xff09;&#xff0c; 因其价格低廉 &#xff0c; 功能强大 &#xff0c; 在实际应用中得到广泛认可 . 本设计…

mac如何安装nvm

​ vue项目开发&#xff0c;热更新&#xff0c;webpack&#xff0c;前辈造的轮子&#xff1a;各类的工具&#xff0c;库&#xff0c;像axios,qs,cookie等轮子在npm上可以拿来直接用&#xff0c;需要node作为环境支撑。 开发时同时有好几个项目&#xff0c;每个项目的需求不同…

关于MCU-Cortex M7的存储结构(flash与SRAM)

MCU并没有DDR&#xff0c;所以他把代码存储在flash上&#xff0c;临时变量和栈运行在SRAM上。之所以这么做是因为MCU的CPU频率很低&#xff0c;一般低于500MHZ&#xff0c;flash的读取速度能够满足CPU的取指需求&#xff0c;但flash 的写入速度很慢&#xff0c;所以引入了SRAM …

VSCode神仙插件——CodeSnap (好看的代码截图)

1 安装 2 使用 选中要截图的代码,右键 此时右侧会出现代码截图的预览图 如果要将截图保存到本地,则点击上图红色框中的图标 也可以点击下面截的图,CtrlC复制,然后就可以CtrlV粘贴到其他应用程序里了