SpringBoot启动流程分析3–run方法之prepareEnvironment()方法
一、概述
源码基于SpringBoot 2.7.xx版本
1.1 配置文件及其优先级
-
spring.config.name
配置文件属性名称,默认值:application
-
spring.config.location
配置文件路径,默认值:“optional:classpath:/;optional:classpath:/config/”,
“optional:file:./;optional:file:./config/;optional:file:./config/*/”
SpringBoot提供了3种配置文件的格式
- application.properties(传统格式/默认格式)
- application.yml(主流格式)
- application.yaml
配置文件优先级
application.properties ➡️ application.yml ➡️ application.yaml
不同配置文件中相同配置高优先级覆盖低优先级,不同配置文件中不同配置全部保留,application.properties优先级最高
配置文件位置优先级
- file:./config/*/
- *表示只有一级目录,优先级最高
- file:./config/
- jar包所在目录下config目录
- file:./
- jar包所在目录
- classpath:/config/
- resources目录下的config目录
- classpath:/
- resources目录,优先级最低
二、详解
2.1 run方法之prepareEnvironment()方法
构建应用上下文环境–ConfigurableEnvironment
public class SpringApplication {
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 创建并配置相应的环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 根据用户启动参数,配置 environment系统环境,将main 函数的args封装成 SimpleCommandLinePropertySource 加入环境中。
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 启动相应的监听器,其中一个重要的监听器 EnvironmentPostProcessorApplicationListener(SpringBoot2.4+) 就是加载项目配置文件的监听器,
// ConfigFileApplicationListener已过时。
listeners.environmentPrepared(bootstrapContext, environment);
// 将'defaultProperties'propertySource移到最后
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
// 把当前的environment和application进行对应的绑定。
bindToSpringApplication(environment);
// 转换Environment
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
}
2.2 getOrCreateEnvironment()
创建应用上下文环境
public class SpringApplication {
private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
private ConfigurableEnvironment getOrCreateEnvironment() {
// 第一次进来肯定为null了
if (this.environment != null) {
return this.environment;
}
// 根据之前进行webType的类型来进行环境的配置
ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(this.webApplicationType);
if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) {
environment = ApplicationContextFactory.DEFAULT.createEnvironment(this.webApplicationType);
}
return (environment != null) ? environment : new ApplicationEnvironment();
}
}
class DefaultApplicationContextFactory implements ApplicationContextFactory {
@Override
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
return getFromSpringFactories(webApplicationType, ApplicationContextFactory::createEnvironment, null);
}
private <T> T getFromSpringFactories(WebApplicationType webApplicationType,
BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {
// ApplicationContextFactory默认加载的实现类为
// AnnotationConfigReactiveWebServerApplicationContext.Factory
// AnnotationConfigServletWebServerApplicationContext.Factory
// 根据webApplicationType创建为ApplicationServletEnvironment
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.Factory
for (ApplicationContextFactory candidate : SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class,
getClass().getClassLoader())) {
T result = action.apply(candidate, webApplicationType);
if (result != null) {
return result;
}
}
return (defaultResult != null) ? defaultResult.get() : null;
}
}
spring.factories的配置:
# Application Context Factories
org.springframework.boot.ApplicationContextFactory=\
org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext.Factory,\
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.Factory
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext
implements AnnotationConfigRegistry {
static class Factory implements ApplicationContextFactory {
@Override
public Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : ApplicationServletEnvironment.class;
}
@Override
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : new ApplicationServletEnvironment();
}
}
}
Environment接口提供了4种实现方式,这四种分别代表普通程序,web程序、测试程序和响应式web环境。
- StandardEnvironment
- StandardServletEnvironment
- MockEnvironment
- StandardReactiveWebEnvironment
2.3 ApplicationEnvironmentPreparedEvent
发布应用环境已准备完毕事件
listeners.environmentPrepared(bootstrapContext, environment);
监听该事件ApplicationEnvironmentPreparedEvent的有:
- LoggingApplicationListener
- EnvironmentPostProcessorApplicationListener
EnvironmentPostProcessorApplicationListener–配置文件加载
详情请参考–SpringBoot扩展点–事件监听机制
三、 其他
3.1 printBanner()–打印Banner
public class SpringApplication {
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
}