SpringBoot运行源码分析:Spring应用上下文准备

Spring应用上下文的准备我们在上一节完成了应用上下文的创建工作,SpringApplication 继续通过 prepareContext方法来进行应用上下文的准备工作 。首先,通过图 4-4 来整体了解一下 prepareContext 的核心功能及流程 。

SpringBoot运行源码分析:Spring应用上下文准备

文章插图
 
配合流程图,看一下 SpringApplication 中 prepareContext 方法源代码及功能注解 。
private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironmentenvironment,SpringApplicationRunListenerslisteners ,Applicat ionArguments applicat ionArguments, Banner printedBanner) {//没置上下文的配置环境context . setEnvironment (environment);//应用上下文后置处理postProcessApplicationContext( context);//在 context 刷新之前,Appl icat ionContext Initial izer 初始化 contextapplyInitializers(context);//通知监听器 context 准备完成,该方法以 上为上下文准备阶段,以下为上下文加载阶段listeners . contextPrepared(context);//打印日志,启动 Profileif (this . logStartupInfo)-logStartupInfo(context . getParent() == nu1l);logStartupProfileInfo( context);}//获得 ConfigurableL istableBeanFactory 并炷册单例对象ConfigurableL istableBeanFactory beanFactory = context . getBeanFactory();beanFactory. registerSingleton("springApplicat ionArguments", applicationArguments);if (printedBanner != null) {//注册打印日志对象beanF actory. registerSingleton("springBootBanner", printedBanner);if (beanFactory instanceof DefaultlistableBeanFactory) {//没置是否允许覆盖炷册((DefaultListableBeanFactory) beanFactory). setAllowBeanDefinitionOverriding(this . allowBeanDefinitionOverriding);//获取全部配置源,其中包含 primarySources 和 sourcesSet<0bject> sources = getAllSources();Assert . notEmpty(sources, "Sources must not be empty");//将 sources 中的 Bean 加载到 context 中load(context, sources . toArray(new 0bject[0]));//遁知监听器 context 加载完成listeners . contextLoaded(context);}通过流程图和具体代码可以看出,在该方法内完成了两步操作:应用上下文的准备和加载 。
下面我们针对具体的源代码进行详细讲解 。
应用上下文准备阶段在上下文准备阶段,主要有 3 步操作:对 context 设 置 environment、应用上下文后置处理和 ApplicationContextlnitializer 初始化 context 操作 。
首先是对 context 设置 environment,代码和业务操作都很简单 。
public void setEnvironment (ConfigurableEnvironment environment) {//设置 context 的 environmentsuper. setEnvi ronment( environment);//设置 context 的 reader 属性的 conditionEvaluator 属性 this.readeer. settEnvironment(environment) ;//设置 context 的 scanner 属性的 environment 属性this. scanner. setEnvi ronment ( envi ronment);}随 后 , 便 是 进 行 Spring 应 用 上 下 文 的 后置处理 , 这 一 步 是 通 过postProcessApplicationContext 方法来完成的 。
protected void postProcessApplicat ionContext (ConfigurableApplicat ionConEextcontext){f (this. beanNameGenerator != null) {// 如果 beanNameGenerator 为 null, 则将当前的 beanNameGenerator 按照默认名字进行注册context . getBeanFactory(). regi sterSingleton(Annotat ionConfigUtils .CONF IGURATION BEAN NAME GENERATOR,this . beanNameGenerator);esourceLoader 为 null 时, 则根据 context 的类型分别进行 Resourceloader 和 CLassLoader 的设置if (this .resourceLoader != null) {F (context instanceof GenericApplicationContext) {((GenericApplicationContext) context) . setResourcel oader(this . resourceLoader);if (context instanceof DefaultResourceLoader) {( (DefaultResourceLoader) context). setClassLoader(this.resourceLoader. getClassLoader());//如果为 true 则获取并没置转换服务f (this .addConversionService) {context . getBeanFactory(). setConversionService(ApplicationConversionService . getSharedInstance());}postProcessApplicationContext 方 法 主 要 完 成 上 下 文 的 后 置 操 作 , 默 认 包 含beanNameGeneratorResourceL oader.ClassL oader 和 ConversionService 的设置 。该方法可由子类覆盖实现,以添加更多的操作 。
而在此阶段,beanNameGenerator 和 resourceL oader 都为 null,因此只操作了最后-一步的设置转换服务 。
最后,在通知监听器 context 准备完成之前,通过 applylnitializers 方法对上下文进行初始化 。
所使用的 ApplicationContextInitializer 正是我们在 SpringApplication 初始化阶段设置在itializers 变量中的值,只不过在通过 getlnitializers 方法获取时进行了去重和排序 。
protected void applyInitializers(ConfigurableApplicat ionContext context) {/获取 Appl icat ionContextInitializer 集合并遍历for (ApplicationContextInitializer initializer : getInitializers()) {//解析当前 initial izer.实现的 Appl icat ionContextInitializer 的泛型参数Class<?> requiredType = GenericTypeResolver . resolveTypeArgument(initializer . getClass(), ApplicationContextInitializer.class);1 断言判断所需类似是否与 context 类型匹配Assert. isInstanceOf(requiredType, context, "Unable to call initializer.");// 初始化 contextinitializer. initialize(context);}}


推荐阅读