Spring注解驱动开发第48讲——Spring IOC容器创建源码解析(八)之完成BeanFactory的初始化创建工作,最终完成容器创建
发布日期:2021-06-30 17:56:32 浏览次数:4 分类:技术文章

本文共 3649 字,大约阅读时间需要 12 分钟。

快看我

写在前面

经过前面两讲的学习,我们就把单实例bean的创建流程完完整整地过了一遍,其所牵扯到的步骤还是非常繁琐的,大家一定要耐心跟踪一下Spring的源码,亲自走一遍这个单实例bean的创建流程,相信对你理解Spring的内部原理是极其有帮助的。

在上一讲末尾,我们让程序停留在了下面这行代码处,此时,getBean方法就完全是执行完了,这样,我们单实例bean就被创建出来了。

在这里插入图片描述

而且,创建我们单实例bean的流程牵扯到了很多很多东西,你得亲自跟踪一下Spring的源码才能有所体会,不然说再多也是白费口舌,当然了,你也可以在我上一讲中找到答案。

我们的bean创建出来之后,继续让程序往下运行,可以看到接下来就是通过以下for循环来将所有的bean都创建完。

在这里插入图片描述

那就让它创建其他的bean呗!创建流程不用我再详述一遍吧,跟我们单实例bean(即Blue对象)的创建流程是一模一样的,我们不停地按下F6快捷键让程序不停地往下运行,快速地过一遍就行了,这一过程我也就不再详细地记录了。

当程序运行到下面这行代码处时,上面的那个for循环就整个地执行完了,也就是说,所有的bean都创建完成了。

在这里插入图片描述

让程序继续往下运行,直至运行到下面这行代码处为止。

在这里插入图片描述

可以看到,这儿是来遍历所有的bean,并来判断遍历出来的每一个bean是否实现了SmartInitializingSingleton接口的。哎,你对SmartInitializingSingleton接口还有印象吗?在讲解@EventListener注解的内部原理时,我们就讲解过它,你还记得吗?要是你不记得了,那么可以回顾回顾这一讲。

OK,所有的bean都利用getBean方法创建完成以后,接下来要做的事情就是检查所有的bean中是否有实现SmartInitializingSingleton接口的,如果有的话,那么便会来执行该接口中的afterSingletonsInstantiated方法。

那我们不妨让程序继续往下运行,来验证上面这段话,当程序运行至下面这行代码处时,发现有一个bean实现了SmartInitializingSingleton接口,不然程序是不会进入到if判断语句中的。

在这里插入图片描述

那么,到底是哪一个bean实现了SmartInitializingSingleton接口呢?我们不妨Inspect一下singletonInstance变量的值,这样很快就能知道该bean了,它就是EventListenerMethodProcessor,如下图所示。

在这里插入图片描述

于是,接下来便会执行该bean中的afterSingletonsInstantiated方法,也就是SmartInitializingSingleton接口中定义的方法。

我们继续让程序往下运行,直至执行完整个for循环,由于IOC容器中的bean还是蛮多的,所以要执行完整个for循环,你得不停地按下F6快捷键。当程序运行至下面这行代码处时,我们发现beanFactory.preInstantiateSingletons()这行代码总算是执行完了。

在这里插入图片描述

还记得这行代码是来干嘛的吗?它是来初始化所有剩下的单实例bean的。

接着,我们继续让程序往下运行,直至运行至下面这行代码处为止,此时,程序来到了Spring IOC容器创建的最后一步了,即完成BeanFactory的初始化创建工作。

在这里插入图片描述

以上该方法一旦执行完,那么Spring IOC容器就创建完成了。接下来,我们就看看finishRefresh方法里面都做了些啥事。

完成BeanFactory的初始化创建工作

上面也说了,一旦finishRefresh方法执行完,就意味着完成了BeanFactory的初始化创建工作,顺带脚地,我们Spring IOC容器就创建完成了。

其实,IOC容器在前一步(即finishBeanFactoryInitialization(beanFactory))就已经创建完成了,而且所有的单实例bean也都已经加载完了。这个不用我再叙述一遍了吧😁,不懂的同学,请看前面两讲。

那么,finishRefresh方法里面究竟都做了些啥事呢?我们不妨按下F5快捷键进入该方法里面去看一下,如下图所示,是不是很熟悉啊!这里面的逻辑,我们应该以前都瞟过一眼,只是过去一段时间了,我们似乎都快忘记了。

在这里插入图片描述

初始化和生命周期有关的后置处理器

可以看到finishRefresh方法里面首先会调用一个initLifecycleProcessor方法,该方法是来初始化和生命周期有关的后置处理器的。我们不妨按下F5快捷键进入该方法里面去看一下,如下图所示。

在这里插入图片描述

获取BeanFactory

从上图中可以知道,在initLifecycleProcessor方法里面一开始就是来获取BeanFactory的,而这个BeanFactory,我们之前早就准备好了。

看容器中是否有id为lifecycleProcessor,类型是LifecycleProcessor的组件

按下F6快捷键让程序继续往下运行,会发现有一个判断,即判断BeanFactory中是否有一个id为lifecycleProcessor的组件。我为什么会这么说呢,你只要看一下常量LIFECYCLE_PROCESSOR_BEAN_NAME的值就知道了,如下图所示,该常量的值就是lifecycleProcessor。

在这里插入图片描述

若有,则赋值给this.lifecycleProcessor

如果有的话,那么会从BeanFactory中获取到id为lifecycleProcessor,类型是LifecycleProcessor的组件,并将其赋值给this.lifecycleProcessor。这可以从下面这行代码看出。

在这里插入图片描述

不难发现,首先默认会从BeanFactory中寻找LifecycleProcessor这种类型的组件,即生命周期组件。由于我们是初次与LifecycleProcessor见面,对其还不是很熟悉,所以我们可以点过去看一看它的源码,如下图所示,发现它是一个接口。

在这里插入图片描述

而且,可以看到该接口中还定义了两个方法,一个是onRefresh方法,一个是onClose方法,它俩能够在BeanFactory的生命周期期间进行回调哟😘

如此一来,我们就可以自己来编写LifecycleProcessor接口的一个实现类了,该实现类的作用就是可以在BeanFactory的生命周期期间进行拦截,即在BeanFactory刷新完成以及关闭的时候,回调其里面的onRefresh和onClose这俩方法。

当程序继续往下运行时,很显然,它并不会进入到if判断语句中,而是来到了下面的else分支语句中,这是因为容器在刚开始创建的时候,肯定是还没有生命周期组件的。

若没有,则创建一个DefaultLifecycleProcessor类型的组件,并把创建好的组件注册在容器中

如果没有的话,那么Spring自己会创建一个DefaultLifecycleProcessor类型的对象,即默认的生命周期组件。

然后,把创建好的DefaultLifecycleProcessor类型的组件注册到容器中,所执行的是下面这行代码。

在这里插入图片描述

也就是说,容器中会有一个默认的生命周期组件。这样,我们以后其他组件想要使用生命周期组件,直接自动注入这个生命周期组件即可。

这里,我得多说一嘴,你也不要嫌我烦,所有Spring创建的组件,基本上都是这个逻辑,它把组件创建过来以后,就会添加到容器中,这样就能方便我们程序员来使用了

最后,让程序继续往下运行,直至运行到下面这行代码处为止。

在这里插入图片描述

可以看到,这儿会拿到生命周期组件,然后再回调其onRefresh方法。

回调生命周期处理器的onRefresh方法

从上图中我们可以看到,当程序运行到getLifecycleProcessor().onRefresh();这行代码处时,会先拿到我们前面定义的生命周期处理器(即监听BeanFactory生命周期的处理器),然后再回调其onRefresh方法,也就是容器刷新完成的方法。

发布容器刷新完成事件

我们让程序继续往下运行,运行一步即可,这时,程序来到了下面这行代码处。

在这里插入图片描述

很明显,这儿是来发布容器刷新完成事件的。如何来发布容器刷新完成事件,想必不用我来说了吧!我之前就已经详细讲述过了,要是你还不知道的话,那么可以参考这一讲中的事件发布流程这一小节。

接着,继续让程序往下运行,运行一步即可,这时,程序来到了下面这行代码处。

在这里插入图片描述

这是finishRefresh方法里面的最后一步了,这儿是来干嘛的呢?我也说不清,好像是暴露一些什么MBean的,搞不清就不必深究了,直接略过。

至此,Spring IOC容器的整个创建过程,我就帮大家大致地过了一遍。如我有写的不对的地方,欢迎指出,我一定竭力修改。

转载地址:https://liayun.blog.csdn.net/article/details/114632044 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Spring注解驱动开发第49讲——Spring IOC容器创建源码解析(九)之Spring IOC容器创建源码总结
下一篇:Spring注解驱动开发第47讲——Spring IOC容器创建源码解析(七)之初始化所有剩下的单实例bean(下)

发表评论

最新留言

不错!
[***.144.177.141]2024年04月06日 23时23分24秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

OpenCV实战(二)——答题卡识别判卷 2019-04-30
目标检测神经网络的发展历程(52 个目标检测模型) 2019-04-30
Boundary loss 损失函数 2019-04-30
神经网络调参实战(一)—— 训练更多次数 & tensorboard & finetune 2019-04-30
tensorflow使用tensorboard进行可视化 2019-04-30
神经网络调参实战(二)—— activation & initializer & optimizer 2019-04-30
凸优化 convex optimization 2019-04-30
数据库索引 & 为什么要对数据库建立索引 / 数据库建立索引为什么会加快查询速度 2019-04-30
IEEE与APA引用格式 2019-04-30
research gap 2019-04-30
pytorch训练cifar10数据集查看各个种类图片的准确率 2019-04-30
Python鼠标点击图片,获取点击点的像素坐标 2019-04-30
路径规划(一) —— 环境描述(Grid Map & Feature Map) & 全局路径规划(最优路径规划(Dijkstra&A*star) & 概率路径规划(PRM&RRT)) 2019-04-30
神经网络调参实战(四)—— 加深网络层次 & 批归一化 batch normalization 2019-04-30
数据挖掘与数据分析(三)—— 探索性数据分析EDA(多因子与复合分析) & 可视化(1)—— 假设检验(μ&卡方检验&方差检验(F检验))&相关系数(皮尔逊&斯皮尔曼) 2019-04-30
RRT算法(快速拓展随机树)的Python实现 2019-04-30
路径规划(二) —— 轨迹优化(样条法) & 局部规划(人工势能场法) & 智能路径规划(生物启发(蚁群&RVO) & 强化学习) 2019-04-30
D*算法 2019-04-30
强化学习(四) —— Actor-Critic演员评论家 & code 2019-04-30
RESTful API 2019-04-30