基于前文对springcloud的引导,本文则从源码角度查阅下cloud的context板块的运行逻辑html
springcloud是基于springboot开发的,因此读者在阅读此文前最好已经了解了springboot的工做原理。本文将不阐述springboot的工做逻辑java
springboot cloud context在官方的文档中在第一点被说起,是用户ApplicationContext的父级上下文,笔者称呼为BootstrapContext。根据springboot的加载机制,不少第三方以及重要的Configuration配置均是保存在了spring.factories文件中。
笔者翻阅了spring-cloud-context模块下的对应文件,见以下web
# 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
涉及的主要分三类,笔者优先分析监听器,其通常拥有更高的优先级并跟其余两块有必定的关联性。
除了日志监听器笔者不太关注,其他两个分步骤来分析spring
重启监听器,应该是用于刷新上下文的,直接查看下其复写的方法bootstrap
@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类有关,这个就后文再分析好了缓存
按照顺序分析此监听器springboot
1.优先看下其类结构app
public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered { }
此监视器是用于响应ApplicationEnvironmentPreparedEvent应用环境变量预初始化事件,代表BootstrapContext的加载时机在用户上下文以前,且其加载顺序比ConfigFileApplicationListener监听器超前,这点稍微强调下。ide
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); }
逻辑很简单,笔者梳理下
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; }
此处也对上述的代码做下简单的小结
经过上述的代码都可以得知,bootstrapContext也是经过springboot常见的SpringApplication方式来建立的,但其确定有特别的地方。
特别之处就在BootstrapImportSelectorConfiguration类,其也与上述spring.factories文件中org.springframework.cloud.bootstrap.BootstrapConfiguration的Key有直接的关系,咱们下文重点分析
因为继续分析会致使篇幅过长,遂片断式,这样有助于深刻理解以及后期回顾。下文便会主要分析下bootstrapContext额外的特色。