调试途中,发现初始化发生了两次,最后发现是在springboot启动的prepareEnvironment过程中,在LoggingApplicationListener执行之前,会有一个BootstrapApplicationListener先处理ApplicationEnvironmentPreparedEvent
BootstrapApplicationListener是一个通过在单独的引导上下文(bootstrap context)中委托 ApplicationContextInitializer bean 来准备 SpringApplication(例如填充其环境)的侦听器。
bootstrap context是从 spring.factories 中定义为 BootstrapConfiguration 的源创建的 SpringApplication,此刻配置文件读到的是bootstrap.properties或bootstrap.yml的内容,而不是application.properties
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
//spring.cloud.bootstrap.enabled为false则不进行引导
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;
}
ConfigurableApplicationContext context = null;
//读取{spring.cloud.bootstrap.name:bootstrap}默认是bootstrap
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));
}
apply(context, event.getSpringApplication(), environment);
}
- spring.cloud.bootstrap.enabled为false则不进行引导
- 读取{spring.cloud.bootstrap.name:bootstrap}默认是bootstrap作为配置文件
- 创建引导类上下文环境
- 在3中通过
final ConfigurableApplicationContext context = builder.run();
调用run方法
所以如果有读者调试的时候发现第一次通过LoggingApplicationListener的各个阶段找不到自己配置的logback配置文件,原因是先进行了
BootstrapApplicationListener的事件处理,此刻的配置文件和环境变量都是bootstrap的,所以读不到我们自己的配置文件和环境变量(application)
bootstrap和application有什么区别
在springcloud的源码文档中有说明
- Spring Cloud 构建于 Spring Boot 之上,在 Spring Boot 中有两种上下文,一种是 bootstrap,另外一种是 application
- application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置。
- bootstrap 是应用程序的父上下文,也就是说 bootstrap 加载优先于 applicaton。
- bootstrap 主要用于从额外的资源来加载配置信息,还可以在本地外部配置文件中解密属性。
- 这两个上下文共用一个环境,它是任何Spring应用程序的外部属性的来源。
- bootstrap 里面的属性会优先加载,它们默认也不能被本地相同配置覆盖。
- boostrap 由父 ApplicationContext 加载,比 applicaton 优先加载
- boostrap 里面的属性不能被覆盖
bootstrap 配置文件有以下几个应用场景:
- 使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;apollo做配置中心时同理
- 些固定的不能被覆盖的属性
- 些加密/解密的场景;
启动上下文时,Spring Cloud会创建一个Bootstrap Context,作为Spring应用的Application Context的父上下文。初始化的时候,Bootstrap Context==负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的Environment。Bootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖。 Bootstrap context和Application Context有着不同的约定,所以新增了一个bootstrap.yml文件,而不是使用application.yml (或者application.properties)。保证Bootstrap Context和Application Context配置的分离。