Spring 读取 Properties 实现原理
public class SpringApplication {
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
// ...
}
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// ...
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
}
上述代码可知,SpringApplication 在启动的时候先创建 Environment
,然后再配置 Profiles
,然后再添加 configurationProperties
。一般情况下创建的都是 StandardServletEnvironment
环境。
| Create Environment & Configure Profiles | --> | Attach Configure Properties |
(1) Create Environment & Configure Profiles
StandardServletEnvironment
这个类位于 Spring
项目下,它默认在构造器内部就执行 customizePropertySources
方法读取相关的 properties
文件。
它的定义及其相关父类的定义如下:
public class StandardEnvironment extends AbstractEnvironment {
/** 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";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
}
// 构造器内部就开始自定义 property sources
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
}
可见如果 ENV
或者 System.properties
中有 spring.profiles.active
的话,那么 SpringApplication 在启动的时候,就会第一次读取到 spring.profiles.active
的值。然后在 Spring-Boot-2.0.6
版本中会通过 environment.setActiveProfiles()
方法告诉 environment
现在激活的 profile
;但是在 v2.5.0-M1
版本中,并没有告诉现在激活的 profile
。
(2) Attach Configure Properties
Attach configurationProperties
的过程如下:
public final class ConfigurationPropertySources {
private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";
public static void attach(Environment environment) {
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
// ...
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
}
这个 configurationProperties
的数据源就是本地 application.properties
、application.yml
类似于这些的文件源。
读取 Property 过程
getProperty
背后是通过 propertyResolver
实现的:
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
@Override
@Nullable
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}
}
找到就直接返回:
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
Object value = propertySource.getProperty(key);
if (value != null) {
// ...
return convertValueIfNecessary(value, targetValueType);
}
}
}
return null;
}
}
Properties 文件加载顺序
配置文件优先级从高到低顺序↓
file
:./config/
- 优先级最高(项目根路径下的config)file
:./
- 优先级第二 -(项目根路径下)classpath
:/config/
- 优先级第三(项目resources/config下)classpath
:/
- 优先级第四(项目resources根目录)
SpringBoot 项目启动会去扫面项目以上目录位置的 application.yml
或 application.properties
文件。
以上位置的 application.yml
或 application.properties
遵循:
- 高优先级配置会覆盖低优先级配置
- 多个配置文件互补