SpringBoot成长记6(准备SpringContext容器)

SpringBoot成长记6(准备SpringContext容器)
文章图片

上一节的创建了容器对象,核心就是创建了Context和BeanFactory对象,内部初始化了Reader和Scanner,加载了一些内部Bean等。
已经分析的逻辑代码如下:

public ConfigurableApplicationContext run(String... args) { //DONE 扩展点 SpringApplicationRunListeners listeners.starting(); //DONE 配置文件的处理和抽象封装 ConfigurableEnvironment//容器相关处理 //1)核心就是创建了Context和BeanFactory对象,内部初始化了Reader和Scanner,加载了一些内部Bean context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] {ConfigurableApplicationContext.class }, context); //2) TODO 容器对象中还需要准备哪些东西? prepareContext(context, environment, listeners, applicationArguments,printedBanner); //3) TODO refreshContext(context); //其他逻辑 }

这一节我们来看看,创建容器后,容器对象中还需要准备或者说设置哪些东西,并且还执行了容器哪些扩展点呢,一起来看下吧!
prepareContext()的核心脉络 prepareContext()说白了点其实就是给容器Context和容器DefaultListableBeanFactory设置一些属性。 你带着这个思路去理解,就会抓大放小,关注核心即可。大致如下图:
SpringBoot成长记6(准备SpringContext容器)
文章图片

那么接下来,就来看下代码到底做了些什么?
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }
先给大家概括一张图,主要可以划分为三部分非常重要的操作、值得一提的操作、不重要的操作,如下图:
SpringBoot成长记6(准备SpringContext容器)
文章图片

接下来分别带大家一起仔细分析下。
触发的扩展点(非常重要的操作) 1)applyInitializers()触发的扩展点操作
这个扩展操作,其实就是执行了List的一系列扩展操作。你还记得么?
这个List是在new SpringApplication的时候扫描到的类。还记得下图么:
SpringBoot成长记6(准备SpringContext容器)
文章图片

那么重点是这些扩展操作做了什么呢?
其实概括成一句话就是:Initializers之后,为context初始化了2个BeanFactoryPostProcessor ,补充了3个ApplicationLisenter
具体每一个ApplicationContextInitializer的执行你可以通过断点自己仔细看下知道了,其实没有什么复杂的逻辑。
整体如下图所示:
SpringBoot成长记6(准备SpringContext容器)
文章图片

术语普及BeanFactoryPostProcessor是什么?
BeanFactoryPostProcessor是什么? 之前我们遇见过扩展点有SpringApplicationRunListeners和ApplicationContextInitializer SpringApplicationRunListeners通过一个EventPublishingRunListener,对一个List做不同的事件广播做对流程、容器和配置进行 扩展。ApplicationContextInitializer 通过List 其实也是整个流程中的一个扩展。而这里BeanFactoryPostProcessor其实对容器特有的扩展,或者说是增强处理。 在容器的使用过程中,执行List 对应的方法,进行扩展点的执行。很多第三方框架就是从这地方入手进行扩展的,之后会看到的。

2) 触发listener对容器扩展操作。
除了上面ApplicationContextInitializer的扩展执行,另一个扩展操作的执行就是SpringApplicationRunListeners的扩展了。
主要有两次触发,listeners#contextPrepared()和listeners.contextLoaded(context);
之前我们分析过,SpringApplicationRunListeners通过一个EventPublishingRunListener,对一个List做不同的事件广播做对流程、容器和配置进行扩展。这里广播的是contextPreparedEvent,contextLoadedEvent。
具体细节也不在这里展开了,简单的,这两个事件分别可以概括为如下两句话:
contextPreparedEvent的这里执行了2个ApplicationListener的实现,只不过这两个listener的onApplicationEvent几乎是什么都没做,只是注册两个日志对象到容器DefaultListableBeanFactory的singletonObjects属性。
contextLoadedEvent执行了,4个ApplicationListener,其中1个Listener往容器Context中增加了BeafactoryPostProcessor其余四个Listener基本上什么都没干。
SpringBoot成长记6(准备SpringContext容器)
文章图片

可以看出,这几个扩展点核心其实也没有做很复杂的事情,就是给容器对象补充设置了一些属性而已。可以概括如下图所示:
SpringBoot成长记6(准备SpringContext容器)
文章图片

beanFactory的一些属性补充(值得一提的操作) 除了上述比较重要的操作外,prepareContext中还有一些比较值得一提的操作。让我们
1)beanFactory.registerSingleton
注册两个对象到容器springApplicationArguments、springBootBanner到beanFactory的 singletonObjects 属性
2)补充BeanDefinition
BeanDefinitionLoader.load() 补充了 LearnSpringBootApplication 的BeanDefinition到beanFactory中。
这些都比较简单,整体如下图:
SpringBoot成长记6(准备SpringContext容器)
文章图片

其实这里关键的是容器内的两个属性的设置:
一个是【核心属性】Map singletonObjects 容器存放Bean对象的集合
一个是【核心属性】Map beanDefinitionMap 容器存放beanDefinition对象的集合
这个是我们这里想要强调的一点。
设置几个属性或者组件(不重要的操作) 1)context\#setEnvironment () 设置envrioment 到context中,也就是让容器持有配置文件的封装对象而已。
2)resourceLoader 设置resource类加载器 到context容器 ,默认没有resourceLoader ,所以这里什么没干。
3)addConversionService 添加转换器和格式化器 到context容器,不知道这个组件时做啥的,暂时不是很重要。
4)logStartupInfo() 输出启动日志-PID,启动文件目录
5)logStartupProfileInfo() 输出启动日志-使用的profile
6)设置容器属性lazyInitialization、allowBeanDefinitionOverriding,默认都是false,不懒加载和不覆盖BeanDefinition。
SpringBoot成长记6(准备SpringContext容器)
文章图片

小结 说白了,prepareContext()就是给容器Context、BeanFactory设置了一堆属性和组件,执行了initialize/listener的扩展点。
主要给容器如下几个核心属性设置值:
singletonObjects 、beanDefinitionMap 、beanFactoryPostProcessors、applicationListeners。
【SpringBoot成长记6(准备SpringContext容器)】prepareContext()准备完成之后,接下来就是容器关键的扩展操作执行了,也是很多容器功能和第三方功能的扩展之处,我们下一节来一起看下吧!
本文由博客一文多发平台 OpenWrite 发布!

    推荐阅读