spring-boot源码分析之beanFactory · 伍

云中志

共 2811字,需浏览 6分钟

 ·

2021-09-12 22:01

前言

原本是打算昨天把prepareContext方法梳理完,今天开始啃硬骨头——refresh方法的,结果大家昨天就知道了:由于篇幅的问题,剩余内容放在今天分享了。

然后今天的想法是除了昨天需要补充的内容外,看能不能把refresh的内容也分享一部分,毕竟是硬骨头,然而现实情况是,在我把昨天需要补充的内容梳理之后,发现剩余的内容还不少,所以就只能先不开始新的内容了,等prepareContext方法彻底剖析完毕之后我们再啃硬骨头。

下面就让我们看下prepareContext的剩余内容吧!

prepareContext补充

下面这些内容原本是昨天要一起分享的,但是由于内容比较多,而且昨天时间有点晚了,所以就没来得及分享,下面我们就先来做一些简单的补充说明。

创建BeanDefinitionLoader

首先是创建bean的定义加载器,创建beanDefiniton加载器的时候,首先是一个赋值操作,然后创建了AnnotatedBeanDefinitionReader,它的创建就稍微有点复杂。

实例化ConditionEvaluator

创建AnnotatedBeanDefinitionReader的时候要先创建ConditionEvaluator,而ConditionEvaluator实例化的时候又需要先实例化ConditionContextImpl,这里的ConditionEvaluatorConditionContextImplspring boot引入的新特性,主要用于条件配置,我们可以通过@Conditional注解使用这一特性,后面专门来分析吧(又发现新大陆了……)。

最后一步都是赋值操作,就没啥好讲的了。

另外,在load方法中调用了另一个方法registerAnnotationConfigProcessors。就说这方法看着眼熟,原来我们昨天分析beanDefinitionMap初始化的时候,已经研究过了。 

这里注册完成后beanDefinitionMap会多出5个元素。 

下面两个Reader的初始化就不再详细分析了,基本上都是简单的实例化和赋值操作: 

实例化scanner

后面两个操作有必要讲一下,首先实例化了scanner,在实例化过程中,注册了三个注解类型过滤器(AnnotationTypeFilter

事实上,javax.inject.Named,并没有被注册成功,因为当前class并不存在,关于这一点,下面的截图可以很清楚地说明,因为在注册JSR-330class时候报了java.lang.ClassNotFoundException异常,所以最终的结果就是@Component@ManagedBean这两种注解类型拦截器被成功注入,我们通过includeFilters的大小也可以看出这一点。

注册排除过滤器

排除过滤器(excludeFilters)就是过滤不需要被扫描的资源。所以这一步的操作就比较简单了,就是往scanner的 排除过滤器(excludeFilters)中增加了一个ClassExcludeFilter,传入的是sources,目前sources只有我们spring boot项目的主类。

这里需要注意的是,excludeFilters本身是个List,所以这里add的时候其实是把新的排除注册器放在excludeFilters的最前面。

设置loader属性

下面就是这只loader的属性,包括beanNameGenneratorresourceLoaderenvironment。但是由于这些参数都是空的,所以默认情况下这些设置方法并不会被执行:

loader加载资源

下面是load方法最核心的执行流程,核心部分就是注册Bean

由于一张截图无法完整体现registerBean的流程,所以我分成了两张截图,这里最核心的就是doRegisterBean方法,这个方法内部会构建当前classBeanDefinition。其中AnnotatedGenericBeanDefinition是根据注解信息生成的beandefinition信息,包括beanClassAnnotationMetadata(注解元数据)、scope(范围)等,总之就是凡是通过注解方式配置的类,都是通过AnnotatedGenericBeanDefinition来构建它的definition信息的,它本身也就是为了支持注解元数据而生的。

上面这些操作执行完毕后会往beanFactorybeanDefinitionMapbeanDefinitionNames分别保存beanClassbeanNamebeanDefinition信息

执行事件监听

这里调用的是监听器容器的contextLoaded方法,其方法内部本质上是触发对应的事件,从方法名上我们可以看出来这个方法其实发的就是容器已经加载完成的事件。下面是Logger

debug过程中,我发现这里触发的时间类型是ApplicationPeparedEvent事件,这里的事件类别挺多的,不同的监听器会执行各自的onApplicationEvent方法,上面的流程中我们就是以LoggingApplicationListener为例,剖析其执行流程的

对于LoggingApplicationListener总共会处理5种事件,这里处理的是ApplicationPreparedEvent事件,也就是我们前面看到的类型

在当前事件类型下,LoggingApplicationListeneronApplicationEvent方法会往容器中注册两个单例实例,中间的logFile为空,所以并未成功注入。

总结

好了,到今天我们算是把prepareContext方法的内容梳理完了,整体来看还算清晰。

说实话,我是没想到一小块补充内容能有这么多,但事实确实如此,这也从侧面说明了spring boot本身确实比较复杂,任何一小行代码,其内部可能都潜藏着核心的实现代码。我数了一下,不算括号和if语句,只有17行,但是就这短短的17行代码,我用了近三千字还没有说的特别清楚,我自己都不敢想象,不过好的一点是,虽然中间磕磕绊绊,但也算是啃下来了,不过更硬的骨头还在后头——refresh方法才是重头戏,还是不能松懈呀~

最后,我想说的是,任何一小行代码很有可能就是解开你心中困惑的钥匙,所以不论是坑,还是意外收获,更多时候都需要我们自己亲自蹚~

- END -


浏览 36
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报