springcloud情操陶冶-bootstrapContext(一)
基于前文对springcloud的引导,本文则从源码角度查阅下cloud的context板块的运行逻辑
前言
springcloud是基于springboot开发的,所以读者在阅读此文前最好已经了解了springboot的工作原理。本文将不阐述springboot的工作逻辑
Cloud Context
springboot cloud context在官方的文档中在第一点被提及,是用户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
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
org.springframework.cloud.context.restart.RestartListener
# Bootstrap components
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
public void onApplicationEvent(ApplicationEvent input) {
// 应用预备事件,先缓存context
if (input instanceof ApplicationPreparedEvent) {
this.event = (ApplicationPreparedEvent) input;
if (this.context == null) {
this.context = this.event.getApplicationContext();
}
}
// 上下文刷新结束事件,重新传播ApplicationPreparedEvent事件
else if (input instanceof ContextRefreshedEvent) {
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.优先看下其类结构
public class BootstrapApplicationListener
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
}
此监视器是用于响应ApplicationEnvironmentPreparedEvent应用环境变量预初始化事件,表明BootstrapContext的加载时机在用户上下文之前,且其加载顺序比ConfigFileApplicationListener监听器超前,这点稍微强调下。
2.接下来分析下其复写的方法onApplicationEvent(ApplicationEnvironmentPreparedEvent event)
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
// 获取环境变量对象
ConfigurableEnvironment environment = event.getEnvironment();
// 读取spring.cloud.bootstrap.enabled环境属性,默认为true。可通过系统变量设置
if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
true)) {
return;
}
// don't listen to events in a bootstrap context
if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
return;
}
// 寻找当前环境是否已存在BootstrapContext
ConfigurableApplicationContext context = null;
String configName = environment
.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
.getInitializers()) {
if (initializer instanceof ParentContextApplicationContextInitializer) {
context = findBootstrapContext(
(ParentContextApplicationContextInitializer) initializer,
configName);
}
}
// 如果还没有被创建,则开始创建
if (context == null) {
context = bootstrapServiceContext(environment, event.getSpringApplication(),
configName);
// 注册注销监听器
event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(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的创建过程,源码比较长,但笔者认为还是很有必要拿出来
/**
*
* create bootstrap context
*
* @param environment 全局Environment
* @param application 用户对应的Application
* @param configName bootstrapContext对应配置文件的加载名,默认为bootstrap
* @return bootstrapContext
*/
private ConfigurableApplicationContext bootstrapServiceContext(
ConfigurableEnvironment environment, final SpringApplication application,
String configName) {
// create empty environment
StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
MutablePropertySources bootstrapProperties = bootstrapEnvironment
.getPropertySources();
for (PropertySource<?> source : bootstrapProperties) {
bootstrapProperties.remove(source.getName());
}
// 读取spring.cloud.bootstrap.location属性,一般通过系统变量设置,默认为空
String configLocation = environment
.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
Map<String, Object> bootstrapMap = new HashMap<>();
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(
new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
for (PropertySource<?> source : environment.getPropertySources()) {
if (source instanceof StubPropertySource) {
continue;
}
bootstrapProperties.addLast(source);
}
// use SpringApplicationBuilder to create bootstrapContext
SpringApplicationBuilder builder = new SpringApplicationBuilder()
// 此处activeProfiles是通过系统变量设置的,此处稍微备注下
.profiles(environment.getActiveProfiles())
.bannerMode(Mode.OFF)
// 应用bootstrap本身的环境变量
.environment(bootstrapEnvironment)
// Don't use the default properties in this builder
.registerShutdownHook(false).logStartupInfo(false)
.web(WebApplicationType.NONE);
final SpringApplication builderApplication = 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
final ConfigurableApplicationContext context = builder.run();
// 设置bootstrapContext的别名为bootstrap
context.setId("bootstrap");
// 配置bootstrapContext为用户Context的父类
addAncestorInitializer(application, context);
// 合并defaultProperties对应的变量至childEnvironment
bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
return context;
}
此处也对上述的代码作下简单的小结
- 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额外的特点。
springcloud情操陶冶-bootstrapContext(一)的更多相关文章
- springcloud情操陶冶-bootstrapContext(二)
承接前文监听器对bootstrapContext创建的引导,笔者了解到其主要入口类为BootstrapImportSelectorConfiguration.本文将基于此类进行简单的分析 Bootst ...
- springcloud情操陶冶-bootstrapContext(三)
本文则将重点阐述context板块的自动配置类,观察其相关的特性并作相应的总结 自动配置类 直接查看cloudcontext板块下的spring.factories对应的EnableAutoConfi ...
- springcloud情操陶冶-springcloud config server(三)
承接前文springcloud情操陶冶-springcloud config server(二),本文就不讲述server了,就简单阐述下client的应用 前话 config server在引入的时 ...
- springcloud情操陶冶-springcloud config server(一)
承接前文springcloud情操陶冶-springcloud context(二),本文将在前文基础上浅析下ConfigServer的工作原理 前话 根据前文得知,bootstrapContext引 ...
- springcloud情操陶冶-springcloud config server(二)
承接前文springcloud情操陶冶-springcloud config server(一),本文将在前文的基础上讲解config server的涉外接口 前话 通过前文笔者得知,cloud co ...
- springcloud情操陶冶-初识springcloud
许久之前便听到了springcloud如雷贯耳的大名,但是不曾谋面,其主要应用于微服务的相关架构.笔者对微服务并不是很了解,但其既然比较出众,遂也稍微接触研究下 springcloud特性 sprin ...
- SpringMVC源码情操陶冶-FreeMarker之web配置
前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ...
- Spring源码情操陶冶-AbstractApplicationContext#finishBeanFactoryInitialization
承接前文Spring源码情操陶冶-AbstractApplicationContext#registerListeners 约定web.xml配置的contextClass为默认值XmlWebAppl ...
- Spring源码情操陶冶-AbstractApplicationContext#registerListeners
承接前文Spring源码情操陶冶-AbstractApplicationContext#onRefresh 约定web.xml配置的contextClass为默认值XmlWebApplicationC ...
随机推荐
- BZOJ_1132_[POI2008]Tro_计算几何
BZOJ_1132_[POI2008]Tro_计算几何 Description 平面上有N个点. 求出所有以这N个点为顶点的三角形的面积和 N<=3000 Input 第一行给出数字N,N在[3 ...
- 函数strlen()和sizeof的区别
函数strlen()和sizeof的区别: #include<stdio.h> #include<stdlib.h> #include<string.h> #def ...
- 【原创】分布式之redis复习精讲
引言 为什么写这篇文章? 博主的<分布式之消息队列复习精讲>得到了大家的好评,内心诚惶诚恐,想着再出一篇关于复习精讲的文章.但是还是要说明一下,复习精讲的文章偏面试准备,真正在开发过程中, ...
- TensorFlow之DNN(二):全连接神经网络的加速技巧(Xavier初始化、Adam、Batch Norm、学习率衰减与梯度截断)
在上一篇博客<TensorFlow之DNN(一):构建“裸机版”全连接神经网络>中,我整理了一个用TensorFlow实现的简单全连接神经网络模型,没有运用加速技巧(小批量梯度下降不算哦) ...
- vue.js框架原理浅析
vue.js是一个非常优秀的前端开发框架,不是我说的,大家都知道. 首先我现在的能力,独立阅读源码还是有很大压力的,所幸vue写的很规范,通过方法名基本可以略知一二,里面的原理不懂的地方多方面查找资料 ...
- 面试挂在了 LRU 缓存算法设计上
好吧,有人可能觉得我标题党了,但我想告诉你们的是,前阵子面试确实挂在了 RLU 缓存算法的设计上了.当时做题的时候,自己想的太多了,感觉设计一个 LRU(Least recently used) 缓存 ...
- 微信小程序之滑动日历展示
滑动日历效果 效果预览 实现要求:顶部固定悬浮的是获取未来一周的日期,分为上下两部分,上面部分显示星期,下面则显示具体日期.今天则显示今天,可点击头部具体日期,可向左向右滑动. 实现代码 顶部日历 页 ...
- [TCP/IP] TCP的传输连接管理
1.连接建立=>数据传输=>连接释放 2.主动发起连接的是客户端,被动接受连接的是服务器 3.三次握手 客户端 ==> SYN是1同步 ,ACK确认标志是0,seq序号是x ==&g ...
- 万马齐喑究可哀-中文编程的又一波"讨论"
刚申诉了自动折叠, 还是把回答转帖一下: 吴烜:假设中国人最先开发电脑和设计程序语言,那么各种程序语言会使用汉字吗? 这种有明显倾向性的问题怎么还有市场呢...不管谁先开发的电脑(就不论算盘之类是不是 ...
- SQL Server 动态掩码
介绍 动态数据掩码(DDM)是SQL Server 2016引入的一个新功能.目的就是限制没有权限的人去看到一些隐私信息.管理员用户能够决定哪些字段是需要被掩码的,那么如何在不改变应用程序代码的基础上 ...