假如有开发,测试,生产三个不同的环境,需要定义三个不同环境下的配置,以properties
配置为例,在SpringBoot
中会存在下列配置文件
applcation.properties
application-dev.properties
application-test.properties
-
application-online.properties
那么SpringBoot
是如何知道加载哪个配置文件呢,答案就在SpringBoot
环境初始化,也是今天要分析的内容,继续run
方法
//创建ApplicationArguments对象,并将args封装至对象实例
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//创建并配置环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 获取或创建环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 事件广播...这段代码应该很熟悉了吧...
listeners.environmentPrepared(environment);
// 将环境绑定到SpringApplication
bindToSpringApplication(environment);
// 如果当前环境是NONE,则
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
可以看到,环境初始化包含了两个步骤,创建和配置,逐个分析
1.环境初始化-创建环境
//获取或创建环境
private ConfigurableEnvironment getOrCreateEnvironment() {
//当前环境不为空,直接返回
if (this.environment != null) {
return this.environment;
}
//当前应用类型为SERVLET,创建StandardServletEnvironment
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment();
}
//否则创建StandardEnvironment
return new StandardEnvironment();
}
当前分析的代码类型为WebApplicationType.NONE
,我们以new StandardEnvironment()
为例进行分析是如何创建环境的,在此之前,先来看下StandardEnvironment
类的类继承结构
有了类继承关系,就可通过Debug代码,查看new StandardEnvironment()
时具体初始化了哪些东西
1.1 StandardEnvironment
/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
1.2 抽象父类AbstractEnvironment
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
protected Set<String> getReservedDefaultProfiles() {
return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);
}
// 其中有几个常量大家可以预先熟悉下,有助于下面代码分析
// 这个常量大家一定熟悉了,profile的激活配置
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
// 可用在web.xml中
//<context-param>
//<param-name>spring.profiles.default</param-name>
//<param-value>development</param-value>
//</context-param>
public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
// 默认节点名,SpringBoot启动控制台的
// No active profile set, falling back to default profiles: default这句话大家一定不陌生了
protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
// 会保存获取到的激活的profile节点信息
private final Set<String> activeProfiles = new LinkedHashSet<>();
看到RESERVED_DEFAULT_PROFILE_NAME
大家应该明白了,如果不指定profile节点,那么SpringBoot默认选择的是default节点
2.环境初始化-配置环境
// 配置环境
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// 配置PropertySources
configurePropertySources(environment, args);
// 配置Profiles节点
configureProfiles(environment, args);
}
逐步分析
2.1 配置PropertySources
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
} else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
2.2 配置Profiles节点
// 配置Profiles节点
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
// 确保节点已被初始化
environment.getActiveProfiles();
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
// 激活profile节点
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
#################################getActiveProfiles()#################################
// 从当前环境中获取被激活的profile节点
public String[] getActiveProfiles() {
return StringUtils.toStringArray(doGetActiveProfiles());
}
// 从当前环境中获取被激活的profile节点
protected Set<String> doGetActiveProfiles() {
synchronized (this.activeProfiles) {
if (this.activeProfiles.isEmpty()) {
// ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
// 本例并未指定spring.profiles.active属性,所以这里获取到的是null
String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.activeProfiles;
}
}
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
#################################setActiveProfiles()#################################
// 这段代码很简单了,把激活的profile节点放入activeProfiles集合
public void setActiveProfiles(String... profiles) {
Assert.notNull(profiles, "Profile array must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Activating profiles " + Arrays.asList(profiles));
}
synchronized (this.activeProfiles) {
this.activeProfiles.clear();
for (String profile : profiles) {
validateProfile(profile);
this.activeProfiles.add(profile);
}
}
}
到此.我们就分析了SpringBoot
启动时候环境初始化过程,本例并没有配置多环境,大家可以配置多环境,跟踪分析代码...