从根儿上学习spring 九 之run方法启动第四段(3)

embedded/2024/9/23 15:17:22/

图5

接着上一篇的从根儿上学习spring 四(2)--- run方法启动第四段,我们继续分析AbstractBeanFactory#doGetBean方法。

图5-263行

isPrototypeCurrentlyInCreation(beanName)是判断字符串beanName是否存在于线程变量prototypesCurrentlyInCreation中,如果存在则报循环依赖的错。什么意思呢?想必大家都知道spring的bean的作用域有singleton和prototype等,这里的isPrototypeCurrentlyInCreation(beanName)方法就是判断当前beanName实例的作用域是否是prototype的且已经处于创建中了。为什么作用域是prototype的处于创建中了就要报错而前面我们分析singleton的时候只是由于判断处于创建中时则从earlySingletonObjects容器中获取提前暴露的实例呢?

原因很简单,因为prototype类型的bean实例每次获取时都要创建新的对象无法提交暴露,因为下次获取的不是之前暴露的对象了。举个例子,有A,B两个类,A依赖B,B依赖A,且A的作用域是prototype。当spring初始化A(1)时候由于A依赖了B,所以spring又转去初始化B。在初始化B的时候又因为B依赖了A又转去初始A,这时候又会生成A(2),所以就会到这A(1)依赖了B,而B却依赖了A(2)导致歧义,所以spring不允许prototype类型的bean有循环依赖。

图5-268到284行

这些行是从父容器中获取bean 我们先略过。

图5-286行

typeCheckOnly是AbstractBeanFactory#doGetBean方法的最后一个参数,根据注释解释的说明是:确定要获取的bean实例是为了类型检查还是为了其他真实使用场景。一般情况下调用doGetBean方法获取的bean实例都是为了真实使用的,像我们上面举得A,B对象的例子获取的A,B对象当然都是为了真实使用的,但有种场景就只是为了做类型检查,比如有时候为了判断FactoryBean里的对象的类型就要先获取FactoryBean再通过getObject方法获取里面的真实对象来判断其类型,这时获取的FactoryBean实例可能就只是为了类型检查并不是为了真实使用。

如果typeCheckOnly的值为false表示获取bean实例不是为了类型检查,就会调用markBeanAsCreated(beanName);方法标记该beanName至少被创建过一次,该方法会把beanName存到alreadyCreated set容器中标记该beanName完成创建/或正在创建。显然图5的代码我们讲完了,为了故事的继续我们继续贴出AbstractBeanFactory#doGetBean方法的其他代码。

图6

图6-291到292行

获取beanDefinition对象,并校验是否是抽象类,如果是抽象类则报错

图6-295到311行

这些行主要处理spring的@DependsOn注解逻辑,该注解只有一个数组类型的value属性,表示当前被注解的类的初始化依赖于该注解value属性中指定的这些bean。所以spring会优先把@DependsOn注解里指定的bean实例化并初始化好。

295行就是获取@DependsOn注解的value属性值,而298行isDependent(beanName, dep)是防止互相依赖的情况,比如A对象dependsOn B对象,而B对象又dependsOn A对象这是不被允许的。

304行就不必多说了,通过getBean(beanName)方法实例化并初始化这些被dependsOn的实例

图7

前面我们已经讲完了311行前面的代码,接着我们继续往下分析。

图7的代码逻辑主要是创建bean实例并初始化,分为两种情况,一:当bean是单例时; 二:当bean是多例时,会有不同的创建bean的逻辑,下面我们按照代码顺序先分析单例的情况。

图7-314到328行

314行首先是判断这个bean是否是单例,判断逻辑是当前这个bean的作用域是否为空或者为singleton,也就是默认情况下bean都是单例的。如果需要改变bean的作用域可以使用@Scope注解来指定。

315到327行作用是调用了getSingleton(String beanName, ObjectFactory singletonFactory)方法,这里传入的是一个lambada表达式指定的函数式接口singletonFactory。我们先看下图8getSingleton方法的逻辑

图8

图8-215行

我们直接从215行说起,前面逻辑很简单就是先从单例容器singletonObjects获取实例,能获取到自然不需要走下面创建逻辑,获取不到且当前bean正在被销毁那自然是冲突的便会给个BeanCreationNotAllowedException异常。beforeSingletonCreation方法主要是向singletonsCurrentlyInCreation容器中添加beanName,在前面讲DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)方法时我们说过通过singletonsCurrentlyInCreation容器我们可以判断当前bean是否之前尝试创建过,就和这里呼应上了。

图8-217到220

这几行是在真正开始创建bean前创建一个抑制异常集合,这个集合的作用是在真正开始创建bean时发生了BeanCreationException异常时都添加到这个集合里,然后当前方法抛出异常时把这些集合里的异常信息全部append追加到一起并打印出来。

图8-222到方法末尾行

该行就是调用单例工厂创建bean实例,这里的单例工厂就是我们图7里通过lambada表达式创建的单例工厂,bean实例创建成功后设置newSingleton为true

该方法的后面代码我就不贴了大家自行看下吧也就是把singletonsCurrentlyInCreation集合清一清,并且把创建好的bean实例放到spring容器里。总的来说这个方法很简单,复杂的是传进来的singletonFactory.getObject()方法的内部创建bean的逻辑。

图7-317行

说了图8的方法,其实最核心的就是调用了singletonFactory.getObject()方法,最终也就是调用了图7的317行:createBean(beanName, mbd, args)方法,接下来我们进到这个方法探究一二。这个方法我用两个图来展示其代码分别是图9和图10。我们先看下图9

图9

图9-467到471行

这几行就是为了解析出当前bean的class类型,不过相信大家跟我一样看着这短短几行代码却给了这么多注释难免不给予多了几分关注。用我的初中阅读理解的水平稍微翻译下这里的注释:确保这时bean的class类型被精确解析,并且在bean 的class类是动态解析出来不能被存储在共享bean definition中的情况下时克隆bean definition,为下面创建bean使用。

翻译了上面的注释,我给些个人的理解,spring的bean defintion被spring解析后是统一维护的,也就是上面说的shared merged bean definition。而此时要用来创建bean实例的bean definition必须确保class已经确定了,但如果该beanDefinition所代表的bean的class是动态解析出来的,那么也就是说bean class是不确定的就不能设置到原来存在于共享空间里的那个bean definition中。因为共享空间的beanDefinition一旦设值了class属性那每次获取都不会变了,所以这时不得不克隆一个临时的bean definition出来使用。

图9-475行

该行主要涉及到spring的一个知识点就是MethodOverride,算是bean 注入的一个扩展点吧。在springboot中对应的使用方式是通过@lookup注解实现。大概使用逻辑是在一个方法上打上@lookup注解,那么该方法会被组装成MethodOverride添加到spring中,在调用被@lookup的注解方法时,spring会先判断@lookup注解是否配置了value属性,如果配置了value属性则会尝试使用getBean(beanName)方法获取bean实例并返回,否则则会使用该方法的返回参数的类型去容器中创建bean实例并返回。所以也就是说这个方法你不需要做任何事,spring会先根据beanName创建bean,没有beanName就根据方法返回值类型创建bean实例。大家可以自行试一下

图10

图10-484行

spring处处是扩展,Object bean = resolveBeforeInstantiation(beanName, mbdToUse);方法也是spring提供的扩展能力,如果该方法返回了bean实例就不会走正常的doCreateBean方法去创建bean实例。resolveBeforeInstantiation(beanName, mbdToUse)方法代码不多逻辑也简单,我们稍微看下

图11

该方法首先使用mbd.beforeInstantiationResolved判断是否已经通过该扩展点生成过该bean实例,这里的扩展点其实是InstantiationAwareBeanPostProcessor接口。该接口的postProcessBeforeInstantiation(Class beanClass, String beanName)方法返回object对象,也就是需要创建的bean实例,也就是图11里1033行会调用的逻辑。如果在InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(Class beanClass, String beanName)方法返回了bean实例,那么就不会执行图10里的doCreateBean的创建bean实例的逻辑。这个扩展点我们先说到这,最主要的就是InstantiationAwareBeanPostProcessor扩展,我们简单聊下该扩展接口。

该接口实现了BeanPostProcessor接口,新定义了三个方法,分别postProcessBeforeInstantiation,postProcessAfterInstantiation,postProcessPropertyValues方法。postProcessBeforeInstantiation表示是在bean实例化之前执行,postProcessAfterInstantiation表示在bean实例化之后执行,postProcessPropertyValues在bean设置属性前执行以便对属性进行检查。大家了解下即可,该类的作用就是这样具体使用到时关注下其具体实现场景即可。

图10-495行

我们再回到图10,doCreateBean(beanName, mbdToUse, args)真正开始创建bean实例并初始化。我们下一节接着分析。


http://www.ppmy.cn/embedded/90774.html

相关文章

【Bigdata】什么是云计算

这是我父亲 日记里的文字 这是他的生命 留下留下来的散文诗 几十年后 我看着泪流不止 可我的父亲已经 老得像一个影子 🎵 许飞《父亲写的散文诗》 云计算是一种通过互联网提供计算资源(如计算能力、存储和应用服务)的技…

【TiDB 社区智慧合集】TiDB 在核心场景的实战应用

作者: 社区小助手 原文来源: https://tidb.net/blog/5cc4ec70 杭州银行 杭州银行采用 TiDB 作为其核心系统数据库,标志着银行资产规模和业务复杂性的大幅增长。通过"分布式透明化"的思考,杭州银行实现了从传统 Orac…

【Qt】如何搭建Qt开发环境

Qt的开发工具 需要搭建Qt开发环境,需要安装3个部分: C编译器(gcc、cl.exe...)注意,这里的C编译器不是指visual studio这种集成开发环境,编译器不等于IDE,编译器只是IDE调用的一个程序。Qt SDK…

python --- 协程

文章目录 1、协程的概念2、迭代器2.1 迭代的概念2.2 可迭代对象2.3 迭代器对象2.4、迭代器应用场景 3、生成器 Generator3.1 创建方法 4、yield from结合asyncio.coroutine实现协程5、使用async和await实现协程6、使用协程实现异步 1、协程的概念 协程Coroutine又称微线程&…

书生大模型实战营——入门岛第3关

任务1-破冰活动 从 https://github.com/InternLM/Tutorial fork一个分支到自己的仓库: 在自己的仓库下获取仓库链接: 下载项目代码到本地: git clone https://github.com/trunks2008/Tutorial.git查看当前分支,是我们要使用的ca…

用Python打造精彩动画与视频, 5.2 安装和设置Manim

5.2 安装和设置Manim Manim 是一个强大的动画库,用于创建高质量的数学动画。它最初由 3Blue1Brown 的 Grant Sanderson 开发,并被广泛用于教育和展示。以下是安装和设置 Manim 的详细步骤。 5.2.1 安装Manim Manim 需要 Python 环境和一些依赖库。在安…

PXE——安装,配置,测试(rhel7环境下)

什么是PXE PXE(Preboot eXecution Environment,预启动执行环境)允许计算机在开机时从网络而非本地硬盘或其他存储设备启动。这种技术主要用于网络启动和自动化安装系统,尤其在需要为大量计算机同时安装操作系统的情况下非常有用。…

图片怎么重命名批量修改?教你几种批量重命名小妙招

图片已成为我们工作、学习和生活中不可或缺的一部分。然而,随着图片数量的激增,如何高效地管理和整理这些图片成为了一个挑战。特别是当需要批量重命名图片时,手动操作不仅费时费力,还容易出错。下面交给大家几种图片批量重名方法…