SpringCloud项⽬是如何读取bootstrap.properties⽂件的?
Spring Cloud项⽬是基于Spring Boot项⽬的,我们创建的Spring Cloud项⽬其实包含了两个Spring容器,⼀个是Spring Cloud的,⼀个
是Spring Boot的,Spring Cloud作为⽗容器。这两个容器都是分开进⾏实例化的,最后关联起来。⼀开始是Spring Boot项⽬启动,然后在环境准备阶段会进⼊到BootstrapApplicationListener这个当中,通过这个会创建⼀个属于Spring Cloud的SpringApplication对象
(与Spring Boot创建异曲同⼯,只不过有⼀些⾃⼰特别的配置⽽已),执⾏SpringApplication对象的run⽅法就会创建⼀个Spring Cloud的容器对象。
这个阶段完成之后,Spring Boot的容器还没有创建,当Spring Boot容器创建完成之后,会执⾏初始化操作(主要就是⼀系列的初始化器),通过这个初始化器,最终完成两个容器的融合(设置⽗⼦关系和合并环境参数操作)。
因此bootstrap.properties这个⽂件的读取其实是分为两个阶段的,⼀个是在Spring Cloud这个容器创建过程中读取⽂件的过程(创建Spring Cloud容器阶段),⼀个是在Spring Boot容器初始化过程中环境参数配置的融合过程(设置⽗⼦容器阶段)。后续Spring Boot就可以获
取Spring Could的Bean以及相关配置了。
创建Spring Cloud容器
org.springframework.cloud.bootstrap.BootstrapApplicationListener
监听事件 读取配置
1. 可以通过设置spring.abled为false,不读取bootstrap配置,但是此配置参数必须在当前之前。(默认情况下
此配置为true)
2. 已经解析过(已经存在名称为bootstrap的MutablePropertySources对象)或者当前正在解析,不需要再进⾏解析
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event){
ConfigurableEnvironment environment = Environment();
if(!Property("spring.abled", Boolean.class,
true)){
return;
}
// don't listen to events in a bootstrap context
PropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)){
return;
}
3. 解析configName,通过读取环境配置属性spring.cloud.bootstrap.name,默认值为bootstrap.
ConfigurableApplicationContext context = null;
String configName = environment
.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
4. 构建ConfigurableApplicationContext对象(BootstrapContext)
for(ApplicationContextInitializer<?> initializer : SpringApplication()
.
getInitializers()){
if(initializer instanceof ParentContextApplicationContextInitializer){
context =findBootstrapContext(
(ParentContextApplicationContextInitializer) initializer,
configName);
}
}
if(context == null){
context =bootstrapServiceContext(environment, SpringApplication(),
configName);
.
addListeners(new CloseContextOnFailureApplicationListener(context));
}
默认情况下,存在以下ApplicationContextInitializer对象(通过SPI机制读取,在SpringApplication对象构造过程中初始化的)
以上实例中没有⼀个是ParentContextApplicationContextInitializer类型,因此会进⼊bootstrapServiceContext⽅法。bootstrapServiceContext创建SpringCloud上下⽂
⾸先创建⼀个StandardEnvironment对象,并移除内部的PropertySource列表信息,也就是⼀个空的配置对象。
private ConfigurableApplicationContext bootstrapServiceContext(
ConfigurableEnvironment environment,final SpringApplication application,
String configName){
StandardEnvironment bootstrapEnvironment =new StandardEnvironment();
MutablePropertySources bootstrapProperties = bootstrapEnvironment
.getPropertySources();
for(PropertySource<?> source : bootstrapProperties){
}
读取bootstrap⽂件的路径,默认为"",也就是当前classpath
配置相关的参数
String configLocation = environment
.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
String configAdditionalLocation = environment
.resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}");
Map<String, Object> bootstrapMap =new HashMap<>();
bootstrapMap.put("fig.name", configName);
// if an app (or test) uses spring.main.web-application-type=reactive, bootstrap
// will fail
// force the environment to use none, because if though it is set below in the
// builder
// the environment overrides it
bootstrapMap.put("spring.main.web-application-type","none");
if(StringUtils.hasText(configLocation)){
bootstrapMap.put("fig.location", configLocation);
}
if(StringUtils.hasText(configAdditionalLocation)){
bootstrapMap.put("fig.additional-location",
configAdditionalLocation);
}
创建名称为bootstrap的MapPropertySource类型资源对象
bootstrapProperties.addFirst(
new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap)); for(PropertySource<?> source : PropertySources()){
if(source instanceof StubPropertySource){
continue;
}
bootstrapProperties.addLast(source);
}
通过构造者模式创建⼀个普通类型WebApplicationType==NONE的SpringApplication对象
// TODO: is it possible or sensible to share a ResourceLoader?
SpringApplicationBuilder builder =new SpringApplicationBuilder()
.ActiveProfiles()).bannerMode(Mode.OFF)
.environment(bootstrapEnvironment)
// Don't use the default properties in this builder
.
registerShutdownHook(false).logStartupInfo(false)
.web(WebApplicationType.NONE);
final SpringApplication builderApplication = builder.application();
MainApplicationClass()== null){
// gh_425:
// SpringApplication cannot deduce the MainApplicationClass here
// if it is booted from SpringBootServletInitializer due to the
// absense of the "main" method in stackTraces.
// But luckily this method's second parameter "application" here
// carries the real MainApplicationClass which has been explicitly
// set by SpringBootServletInitializer itself already.
builder.MainApplicationClass());
}
// 默认不包含因此不会执⾏context refresh
PropertySources().contains("refreshArgs")){
// If we are doing a context refresh, really we only want to refresh the
// Environment, and there are some toxic listeners (like the
// LoggingApplicationListener) that affect global static state, so we need a
// way to switch those off.
builderApplication
.setListeners(Listeners()));
}
添加主类资源BootstrapImportSelectorConfiguration
bootstrap项目
启动bootstrap对应的springapplication对象
builder.sources(BootstrapImportSelectorConfiguration.class);
final ConfigurableApplicationContext context = builder.run();
}
@Configuration
@Import(BootstrapImportSelector.class)
public class BootstrapImportSelectorConfiguration {
}
BootstrapImportSelector⽤于通过SpringFactoriesLoader读取spring.factories⽂件中key为BootstrapConfiguration的资源。另外这个类也实现了DeferredImportSelector接⼝,因此会在所有注解@Configuration的类处理完之后才会进⾏处理。配合@Conditional注解使⽤更⾼效。
BootstrapImportSelector的import过程

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。