spring-boot源码分析之BeanFactory · 陆
共 2691字,需浏览 6分钟
·
2021-09-12 22:01
前言
从今天开始,我们要啃硬骨头了——refreshContext
。这个方法就是我们一直在说的spring boot
最核心的方法,这个方法执行完成后,spring boot
基本上就初始化完了,剩下的方法就是推送启动事件,回调相关的监听器之类的,反正就是吃透refresh
方法,基本上就可以宣告革命胜利了,换句话说,就是如果我们啃下这块硬骨头之后,spring boot
启动这块我们就算彻底剖析完了,剩下的都是查漏补缺的小知识点了,搂草打兔子,顺手的事。
好了,大话就说这么多,下面我们开始干活~
refreshContext
容器的刷新首先是从refreshContext
方法开始的,然后refreshContext
方法内部会调用容器的refresh
方法。
这里 refreshContext
就做了两件事,一个是注册容器关闭钩子函数,另外一个就是刷新容器。
关闭钩子函数可以让我们更优雅地关闭spring boot
容器,这一块后期也专门分享一次;刷新容器方法最终会调用容器的刷新方法,关于这个方法,我们之前已经分享过了,t它的作用就是刷新容器中的持久化资源,这里的资源包括xml
、java
配置、注解、配置文件、数据库等。
下面,我们就来详细看下它的内部实现。我们先看下它的调用流程:
从上面图中我们可以看出来,最终其实调用的是AbstractApplicationContext
的refresh
方法,这个方法内部比较长,总共调用了15
个方法,下面我们就逐一剖析这些方法,不过工作日时间有限,今天可能也分享不了太多内容,具体视情况而定吧。
prepareRefresh
我们先说prepareRefresh
方法,这个方法的作用就是为后面的刷新操作做准备,内部实现如下:
简单解释下它的执行流程:
首先设置
spring boot
的启动时间,获取的是当前时间;然后分别设置
closed
和active
为flase
和true
,这两个属性都是原子类AtomicBoolean
。接着会根据日志设置等级输出日志,不过这里必须是
begug
级别才会输出,如果是trace
等级的,则会输出更详细的日志信息。再然后,它会调用
initPropertySources
方法,这个方法的作用就是进行配置资源初始化初始化,我们等下详细剖析;再下面就是资源的校验,这块也需要展开分析
最后是监听器和监听事件的赋值操作
initPropertySources
initPropertySources
方法进行的就是property
资源的初始化,当前容器并没有重写该方法:
由于 AbstractApplicationContext
的initPropertySources
方法是空实现,而且AnnotationConfigServletWebServerApplicationContext
并没有重写该方法,所以最后调用的是父类GenericWebApplicationContext
的这个方法,父类的方法中最后调用的是ConfigurableWebEnvironment
的配置资源初始化方法:
在方法内部,首先通过getEnvironment
方法获取系统的环境设置,然后同获取到的环境设置进行配置资源初始化,其中servletConfig
的入参直接为null
。
关于这里获取到的ConfigurableWebEnvironment
,我们做一点点扩展补充,ConfigurableWebEnvironment
主要的属性是profiles
和资源解析器,这里的defaultProfiles
表示spring boot
的默认配置文件,activeProfiles
表示spring boot
启动时激活的配置文件,从下面截图也可以很清楚看出来:
关于profile
文件,相比各位小伙伴应该都不陌生,在spring boot
中我们的配置文件就叫profile
,默认情况下的配置文件是application.*
,文件类型可以是properties
文件或者yaml
文件,我们通常通过spring.profiles.active=@profileActive@
(``yml`方式类似)指定需要激活的文件(环境配置)
获取完配置资源之后,会调用ConfigurableWebEnvironment
的initPropertySources
方法,下面是initPropertySources
方法的内部调用流程。在debug
过程中,我发现默认情况下servletContext
和servletConfig
都为空,所以replace
方法实际并未执行。
validateRequiredProperties
这里校验是根据我们AbstractPropertyResolver
解析器的requiredProperties
属性进行判断的,如果存在为空的配置就会报错,说这个方法内部实现也很简单,流程也不是很复杂,就是一些简单的资源获取和空判断:
这里的requiredProperties
和environment
有关,它是AbstractEnvironment
的setRequiredProperties
方法中初始化的,关于这个方法的调用,等我们回头分析environment
初始化的时候再来研究。
监听器赋值
prepareRefresh0
方法的最后就是一些监听器集合的赋值操作,earlyApplicationListeners
表示预刷新容器应用监听器集合(pre-refresh ApplicationListeners
),applicationListeners
表示当前容器监听器集合,earlyApplicationEvents
就表示预刷新应用监听事件。
其实也就是监听器的初始化,代码也很简单,加上注释就很容器理解,我也就不再赘述了。
总结
今天效率有点低呀,一整天就搞定了一个方法,确实有些离谱,但是也没办法,今天事情确实比较多——今天我负责处理oncall
问题,然后下午又开了三个多小时的会,下班那会还在排查处理线上问题,有点难呀~
不过,好在之前已经把今天的内容写的七七八八了,不然今天新内容就悬了……刚刚又看了下剩余的内容,明天应该可以搞定3
个方法,然后还剩11
个方法,后天4
个,大后天4
个,周一安排3个
,完美,所以今天就先到这里吧~