假如有開發(fā),測試,生產(chǎn)三個不同的環(huán)境,需要定義三個不同環(huán)境下的配置,以properties
配置為例,在SpringBoot
中會存在下列配置文件
applcation.properties
application-dev.properties
application-test.properties
-
application-online.properties
那么SpringBoot
是如何知道加載哪個配置文件呢,答案就在SpringBoot
環(huán)境初始化,也是今天要分析的內(nèi)容,繼續(xù)run
方法
//創(chuàng)建ApplicationArguments對象,并將args封裝至對象實例
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//準備環(huán)境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//創(chuàng)建并配置環(huán)境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 獲取或創(chuàng)建環(huán)境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置環(huán)境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 事件廣播...這段代碼應該很熟悉了吧...
listeners.environmentPrepared(environment);
// 將環(huán)境綁定到SpringApplication
bindToSpringApplication(environment);
// 如果當前環(huán)境是NONE,則
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
可以看到,環(huán)境初始化包含了兩個步驟,創(chuàng)建和配置,逐個分析
1.環(huán)境初始化-創(chuàng)建環(huán)境
//獲取或創(chuàng)建環(huán)境
private ConfigurableEnvironment getOrCreateEnvironment() {
//當前環(huán)境不為空,直接返回
if (this.environment != null) {
return this.environment;
}
//當前應用類型為SERVLET,創(chuàng)建StandardServletEnvironment
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment();
}
//否則創(chuàng)建StandardEnvironment
return new StandardEnvironment();
}
當前分析的代碼類型為WebApplicationType.NONE
,我們以new StandardEnvironment()
為例進行分析是如何創(chuàng)建環(huán)境的,在此之前,先來看下StandardEnvironment
類的類繼承結(jié)構(gòu)
有了類繼承關(guān)系,就可通過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";
// 默認節(jié)點名,SpringBoot啟動控制臺的
// No active profile set, falling back to default profiles: default這句話大家一定不陌生了
protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
// 會保存獲取到的激活的profile節(jié)點信息
private final Set<String> activeProfiles = new LinkedHashSet<>();
看到RESERVED_DEFAULT_PROFILE_NAME
大家應該明白了,如果不指定profile節(jié)點,那么SpringBoot默認選擇的是default節(jié)點
2.環(huán)境初始化-配置環(huán)境
// 配置環(huán)境
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// 配置PropertySources
configurePropertySources(environment, args);
// 配置Profiles節(jié)點
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節(jié)點
// 配置Profiles節(jié)點
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
// 確保節(jié)點已被初始化
environment.getActiveProfiles();
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
// 激活profile節(jié)點
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
#################################getActiveProfiles()#################################
// 從當前環(huán)境中獲取被激活的profile節(jié)點
public String[] getActiveProfiles() {
return StringUtils.toStringArray(doGetActiveProfiles());
}
// 從當前環(huán)境中獲取被激活的profile節(jié)點
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節(jié)點放入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
啟動時候環(huán)境初始化過程,本例并沒有配置多環(huán)境,大家可以配置多環(huán)境,跟蹤分析代碼...