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设置一些属性。 你带着这个思路去理解,就会抓大放小,关注核心即可。大致如下图:
那么接下来,就来看下代码到底做了些什么?
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<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
先给大家概括一张图,主要可以划分为三部分非常重要的操作、值得一提的操作、不重要的操作,如下图:
接下来分别带大家一起仔细分析下。
触发的扩展点(非常重要的操作)
1)applyInitializers()触发的扩展点操作
这个扩展操作,其实就是执行了List
这个List
那么重点是这些扩展操作做了什么呢?
其实概括成一句话就是:Initializers之后,为context初始化了2个BeanFactoryPostProcessor ,补充了3个ApplicationLisenter
具体每一个ApplicationContextInitializer的执行你可以通过断点自己仔细看下知道了,其实没有什么复杂的逻辑。
整体如下图所示:
术语普及BeanFactoryPostProcessor是什么?
BeanFactoryPostProcessor是什么?
之前我们遇见过扩展点有SpringApplicationRunListeners和ApplicationContextInitializer
SpringApplicationRunListeners通过一个EventPublishingRunListener,对一个List<ApplicationLisenter>做不同的事件广播做对流程、容器和配置进行 扩展。
ApplicationContextInitializer 通过List<ApplicationContextInitializer> 其实也是整个流程中的一个扩展。
而这里BeanFactoryPostProcessor其实对容器特有的扩展,或者说是增强处理。 在容器的使用过程中,执行List<BeanFactoryPostProcessor> 对应的方法,进行扩展点的执行。很多第三方框架就是从这地方入手进行扩展的,之后会看到的。
2) 触发listener对容器扩展操作。
除了上面ApplicationContextInitializer的扩展执行,另一个扩展操作的执行就是SpringApplicationRunListeners的扩展了。
主要有两次触发,listeners#contextPrepared()和listeners.contextLoaded(context);
之前我们分析过,SpringApplicationRunListeners通过一个EventPublishingRunListener,对一个List
具体细节也不在这里展开了,简单的,这两个事件分别可以概括为如下两句话:
contextPreparedEvent的这里执行了2个ApplicationListener的实现,只不过这两个listener的onApplicationEvent几乎是什么都没做,只是注册两个日志对象到容器DefaultListableBeanFactory的singletonObjects属性。
contextLoadedEvent执行了,4个ApplicationListener,其中1个Listener往容器Context中增加了BeafactoryPostProcessor其余四个Listener基本上什么都没干。
可以看出,这几个扩展点核心其实也没有做很复杂的事情,就是给容器对象补充设置了一些属性而已。可以概括如下图所示:
beanFactory的一些属性补充(值得一提的操作)
除了上述比较重要的操作外,prepareContext中还有一些比较值得一提的操作。让我们
1)beanFactory.registerSingleton
注册两个对象到容器springApplicationArguments、springBootBanner到beanFactory的 singletonObjects 属性
2)补充BeanDefinition
BeanDefinitionLoader.load() 补充了 LearnSpringBootApplication 的BeanDefinition到beanFactory中。
这些都比较简单,整体如下图:
其实这里关键的是容器内的两个属性的设置:
一个是【核心属性】Map<String,Object> singletonObjects 容器存放Bean对象的集合
一个是【核心属性】Map<String, BeanDefinition> 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。
小结
说白了,prepareContext()就是给容器Context、BeanFactory设置了一堆属性和组件,执行了initialize/listener的扩展点。
主要给容器如下几个核心属性设置值:
singletonObjects 、beanDefinitionMap 、beanFactoryPostProcessors、applicationListeners。
prepareContext()准备完成之后,接下来就是容器关键的扩展操作执行了,也是很多容器功能和第三方功能的扩展之处,我们下一节来一起看下吧!
本文由博客群发一文多发等运营工具平台 OpenWrite 发布