基于前文对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(一)的更多相关文章

  1. springcloud情操陶冶-bootstrapContext(二)

    承接前文监听器对bootstrapContext创建的引导,笔者了解到其主要入口类为BootstrapImportSelectorConfiguration.本文将基于此类进行简单的分析 Bootst ...

  2. springcloud情操陶冶-bootstrapContext(三)

    本文则将重点阐述context板块的自动配置类,观察其相关的特性并作相应的总结 自动配置类 直接查看cloudcontext板块下的spring.factories对应的EnableAutoConfi ...

  3. springcloud情操陶冶-springcloud config server(三)

    承接前文springcloud情操陶冶-springcloud config server(二),本文就不讲述server了,就简单阐述下client的应用 前话 config server在引入的时 ...

  4. springcloud情操陶冶-springcloud config server(一)

    承接前文springcloud情操陶冶-springcloud context(二),本文将在前文基础上浅析下ConfigServer的工作原理 前话 根据前文得知,bootstrapContext引 ...

  5. springcloud情操陶冶-springcloud config server(二)

    承接前文springcloud情操陶冶-springcloud config server(一),本文将在前文的基础上讲解config server的涉外接口 前话 通过前文笔者得知,cloud co ...

  6. springcloud情操陶冶-初识springcloud

    许久之前便听到了springcloud如雷贯耳的大名,但是不曾谋面,其主要应用于微服务的相关架构.笔者对微服务并不是很了解,但其既然比较出众,遂也稍微接触研究下 springcloud特性 sprin ...

  7. SpringMVC源码情操陶冶-FreeMarker之web配置

    前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ...

  8. Spring源码情操陶冶-AbstractApplicationContext#finishBeanFactoryInitialization

    承接前文Spring源码情操陶冶-AbstractApplicationContext#registerListeners 约定web.xml配置的contextClass为默认值XmlWebAppl ...

  9. Spring源码情操陶冶-AbstractApplicationContext#registerListeners

    承接前文Spring源码情操陶冶-AbstractApplicationContext#onRefresh 约定web.xml配置的contextClass为默认值XmlWebApplicationC ...

随机推荐

  1. privoxy自动请求转发到多个网络

    有些时候我们需要通过不同的代理访问不同资源,比如某些ip或域名走本地网络,某些ip或域名走不可描述的代理等.当然这只是举个栗子! 我要解决的问题是:我的内网机器没有internet访问权限,但是我的应 ...

  2. javascript的键盘事件大全

    javascript的键盘事件大全 ------------------------------------------------------------------- 使用event对象的keyC ...

  3. BZOJ1500 [NOI2005]维修数列-fhq_Treap

    题面见这里 反正是道平衡树,就拿 fhq_Treap 写了写... 这道题思路基本是围绕“用 Treap 维护中序遍历” 和 中序遍历的性质 来进行的操作 所以就可以类比线段树进行一些操作 1. 建树 ...

  4. java游戏开发杂谈 - 游戏编程浅析

    每个游戏,你所看到的它的一切,都是计算机画出来的! 地图是画出来,人物是画出来的,树木建筑是画出来的,菜单按钮是画出来的,滚动的文字.闪烁的图标.云雾烟火,都是画出来的. 游戏编程,所要做的,就是控制 ...

  5. 面向对象OOP概念描述

    面向对象三大特征:封装,继承,多态 封装就是把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法 继承就是在已经存在的类的定义作为基础,建立新的技术.新定义的类可以添加新的数据或功能,也可以 ...

  6. JavaSE:数据类型之间的转换(附常见面试题)

    数据类型之间的转换 分为以下几种情况: 1)低级到高级的自动类型转换: 2)高级到低级的强制类型转换(会导致溢出或丢失精度): 3)基本类型向类类型转换: 4)基本类型向字符串的转换: 5)类类型向字 ...

  7. GIS之家demo源代码咨询

    GIS之家demo源代码咨询收费服务(希望对 webgis 新人有所帮助) GIS之家QQ群(采取QQ群入群收费模式): GIS之家001:296438295 需要入群的giser们,入群之前联系GI ...

  8. arcgis api 4.x for js 结合 react 入门开发系列初探篇(附源码下载)

    你还在使用 JQuery 或者 Dojo 框架开发 arcgis api 4.x for js 吗?想试试模块化开发吗?随着前端技术的发展,arcgis api 4.x for js 也有了结合 re ...

  9. arcgis api 4.x for js 离线部署

    在我的GIS之家群里,经常遇到 webgis 开发新手们提问 arcgis api for js 如何本地离线部署,而不是直接调用在线的,因为在线模式依赖互联网以及网速环境因素,受到的限制影响比较大. ...

  10. andorid 应用第二次登录实现自动登录

    前置条件是所有用户相关接口都走 https,非用户相关列表类数据走 http. 步骤 第一次登陆 getUserInfo 里带有一个长效 token,该长效 token 用来判断用户是否登陆和换取短 ...