springcloud情操陶冶-bootstrapContext(一)
本文内容纲要:
-前言
-CloudContext
-后记
基于前文对springcloud的引导,本文则从源码角度查阅下cloud的context板块的运行逻辑
前言
springcloud是基于springboot开发的,所以读者在阅读此文前最好已经了解了springboot的工作原理。本文将不阐述springboot的工作逻辑
CloudContext
springbootcloudcontext在官方的文档中在第一点被提及,是用户ApplicationContext的父级上下文,笔者称呼为BootstrapContext。根据springboot的加载机制,很多第三方以及重要的Configuration配置均是保存在了spring.factories文件中。
笔者翻阅了spring-cloud-context模块下的对应文件,见如下
#AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration
#ApplicationListeners
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
org.springframework.cloud.context.restart.RestartListener
#Bootstrapcomponents
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
涉及的主要分三类,笔者优先分析监听器,其一般拥有更高的优先级并跟其他两块有一定的关联性。
除了日志监听器笔者不太关注,其余两个分步骤来分析
RestartListener
重启监听器,应该是用于刷新上下文的,直接查看下其复写的方法
@Override
publicvoidonApplicationEvent(ApplicationEventinput){
//应用预备事件,先缓存context
if(inputinstanceofApplicationPreparedEvent){
this.event=(ApplicationPreparedEvent)input;
if(this.context==null){
this.context=this.event.getApplicationContext();
}
}
//上下文刷新结束事件,重新传播ApplicationPreparedEvent事件
elseif(inputinstanceofContextRefreshedEvent){
if(this.context!=null&&input.getSource().equals(this.context)
&&this.event!=null){
this.context.publishEvent(this.event);
}
}
else{
//上下文关闭事件传播至此,则开始清空所拥有的对象
if(this.context!=null&&input.getSource().equals(this.context)){
this.context=null;
this.event=null;
}
}
}
上述的刷新事件经过查阅,与org.springframework.cloud.context.restart.RestartEndpoint类有关,这个就后文再分析好了
BootstrapApplicationListener
按照顺序分析此监听器
1.优先看下其类结构
publicclassBootstrapApplicationListener
implementsApplicationListener<ApplicationEnvironmentPreparedEvent>,Ordered{
}
此监视器是用于响应ApplicationEnvironmentPreparedEvent应用环境变量预初始化事件,表明BootstrapContext的加载时机在用户上下文之前,且其加载顺序比ConfigFileApplicationListener监听器超前,这点稍微强调下。
2.接下来分析下其复写的方法onApplicationEvent(ApplicationEnvironmentPreparedEventevent)
@Override
publicvoidonApplicationEvent(ApplicationEnvironmentPreparedEventevent){
//获取环境变量对象
ConfigurableEnvironmentenvironment=event.getEnvironment();
//读取spring.cloud.bootstrap.enabled环境属性,默认为true。可通过系统变量设置
if(!environment.getProperty("spring.cloud.bootstrap.enabled",Boolean.class,
true)){
return;
}
//don'tlistentoeventsinabootstrapcontext
if(environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)){
return;
}
//寻找当前环境是否已存在BootstrapContext
ConfigurableApplicationContextcontext=null;
StringconfigName=environment
.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
for(ApplicationContextInitializer<?>initializer:event.getSpringApplication()
.getInitializers()){
if(initializerinstanceofParentContextApplicationContextInitializer){
context=findBootstrapContext(
(ParentContextApplicationContextInitializer)initializer,
configName);
}
}
//如果还没有被创建,则开始创建
if(context==null){
context=bootstrapServiceContext(environment,event.getSpringApplication(),
configName);
//注册注销监听器
event.getSpringApplication().addListeners(newCloseContextOnFailureApplicationListener(context));
}
//加载BoostrapContext上的ApplicationContextInitializers到用户Context上
apply(context,event.getSpringApplication(),environment);
}
逻辑很简单,笔者梳理下
- spring.cloud.bootstrap.enabled用于配置是否启用BootstrapContext,默认为true。可采取系统变量设定
- spring.cloud.bootstrap.name用于加载bootstrap对应配置文件的别名,默认为bootstrap
- BootstrapContext上的beanType为ApplicationContextInitializer类型的bean对象集合会被注册至用户的Context上
3.重点看下BootstrapContext的创建过程,源码比较长,但笔者认为还是很有必要拿出来
/**
*
* createbootstrapcontext
*
*@paramenvironment全局Environment
*@paramapplication用户对应的Application
*@paramconfigNamebootstrapContext对应配置文件的加载名,默认为bootstrap
*@return bootstrapContext
*/
privateConfigurableApplicationContextbootstrapServiceContext(
ConfigurableEnvironmentenvironment,finalSpringApplicationapplication,
StringconfigName){
//createemptyenvironment
StandardEnvironmentbootstrapEnvironment=newStandardEnvironment();
MutablePropertySourcesbootstrapProperties=bootstrapEnvironment
.getPropertySources();
for(PropertySource<?>source:bootstrapProperties){
bootstrapProperties.remove(source.getName());
}
//读取spring.cloud.bootstrap.location属性,一般通过系统变量设置,默认为空
StringconfigLocation=environment
.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
Map<String,Object>bootstrapMap=newHashMap<>();
bootstrapMap.put("spring.config.name",configName);
bootstrapMap.put("spring.main.web-application-type","none");
//加载bootstrapContext配置文件的路径,与spring.config.name搭配使用
if(StringUtils.hasText(configLocation)){
bootstrapMap.put("spring.config.location",configLocation);
}
bootstrapProperties.addFirst(
newMapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME,bootstrapMap));
for(PropertySource<?>source:environment.getPropertySources()){
if(sourceinstanceofStubPropertySource){
continue;
}
bootstrapProperties.addLast(source);
}
//useSpringApplicationBuildertocreatebootstrapContext
SpringApplicationBuilderbuilder=newSpringApplicationBuilder()
//此处activeProfiles是通过系统变量设置的,此处稍微备注下
.profiles(environment.getActiveProfiles())
.bannerMode(Mode.OFF)
//应用bootstrap本身的环境变量
.environment(bootstrapEnvironment)
//Don'tusethedefaultpropertiesinthisbuilder
.registerShutdownHook(false).logStartupInfo(false)
.web(WebApplicationType.NONE);
finalSpringApplicationbuilderApplication=builder.application();
//配置入口函数类
if(builderApplication.getMainApplicationClass()==null){
builder.main(application.getMainApplicationClass());
}
if(environment.getPropertySources().contains("refreshArgs")){
builderApplication
.setListeners(filterListeners(builderApplication.getListeners()));
}
//增加入口类BootstrapImportSelectorConfiguration
builder.sources(BootstrapImportSelectorConfiguration.class);
//create
finalConfigurableApplicationContextcontext=builder.run();
//设置bootstrapContext的别名为bootstrap
context.setId("bootstrap");
//配置bootstrapContext为用户Context的父类
addAncestorInitializer(application,context);
//合并defaultProperties对应的变量至childEnvironment
bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
mergeDefaultProperties(environment.getPropertySources(),bootstrapProperties);
returncontext;
}
此处也对上述的代码作下简单的小结
- spring.cloud.bootstrap.location变量用于配置bootstrapContext配置文件的加载路径,可用System设置,默认则采取默认的文件搜寻路径;与spring.cloud.bootstrap.name搭配使用
- bootstrapContext对应的activeProfiles可采用spring.active.profiles系统变量设置,注意是System变量。当然也可以通过bootstrap.properties/bootstrap.yml配置文件设置
- bootstrapContext的重要入口类为BootstrapImportSelectorConfiguration,此也是下文的分析重点
- bootstrapContext的contextId为bootstrap。即使配置了spring.application.name属性也会被设置为前者,且其会被设置为用户Context的父类
- bootstrap.(yml|properties)上的配置会被合并至用户级别的Environment中的defaultProperties集合中,且其相同的KEY会被丢弃,不同KEY会被保留。即其有最低的属性优先级
通过上述的代码均可以得知,bootstrapContext也是通过springboot常见的SpringApplication方式来创建的,但其肯定有特别的地方。
特别之处就在BootstrapImportSelectorConfiguration类,其也与上述spring.factories文件中org.springframework.cloud.bootstrap.BootstrapConfiguration的Key有直接的关系,我们下文重点分析
后记
由于继续分析会导致篇幅过长,遂片段式,这样有助于深入理解以及后期回顾。下文便会主要分析下bootstrapContext额外的特点。
本文内容总结:前言,CloudContext,后记,
原文链接:https://www.cnblogs.com/question-sky/p/10245384.html