spring boot默认已经配置了很多环境变量,例如,tomcat的默认端口是8080,项目的contextpath是“/”等等,spring boot允许你自定义一个application.properties文件,然后放在以下的地方,来重写spring boot的环境变量
spring对配置application.properties的加载过程:
- 服务启动调用:SpringApplication.run
- 创建默认的环境参数:ConfigurableEnvironment
- 触发事件:ApplicationEnvironmentPreparedEvent
- 完成加载
整个过程主要使用spring boot 内置的ConfigFileApplicationListener监听器监听ApplicationEnvironmentPreparedEvent事件完成对application.properties加载以及设置。
下面我们来跟踪源码,看下spring boot是怎样完成对application.properties文件的加载
- SpringApplication 入口 run:
-
public ConfigurableApplicationContext run(String... args) {
-
//无关的代码暂略
-
.......
-
ConfigurableApplicationContext context = null;
-
FailureAnalyzers analyzers = null;
-
configureHeadlessProperty();
-
//获取执行监听器实例
-
SpringApplicationRunListeners listeners = getRunListeners(args);
-
........
-
//创建全局系统参数实例
-
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
-
args);
-
//创建 ConfigurableEnvironment 并触发ApplicationEnvironmentPreparedEvent事件
-
//加载配置的核心地方,spring启动首要做的事情
-
ConfigurableEnvironment environment = prepareEnvironment(listeners,
-
applicationArguments);
-
.........
-
}
prepareEnvironment方法
-
private ConfigurableEnvironment prepareEnvironment(
-
SpringApplicationRunListeners listeners,
-
ApplicationArguments applicationArguments) {
-
// Create and configure the environment
-
//创建一个配置环境信息,当是web环境时创建StandardServletEnvironment实例,非web环境时创建StandardEnvironment实例
-
ConfigurableEnvironment environment = getOrCreateEnvironment();
-
configureEnvironment(environment, applicationArguments.getSourceArgs());
-
//核心事件触发方法,此方法执行后会执行所有监听ApplicationEnvironmentPreparedEvent事件的监听器,这里我们是跟踪application.properties文件的加载,就查看ConfigFileApplicationListener监听器都做了什么工作
-
listeners.environmentPrepared(environment);
-
if (!this.webEnvironment) {
-
environment = new EnvironmentConverter(getClassLoader())
-
.convertToStandardEnvironmentIfNecessary(environment);
-
}
-
return environment;
-
}
- ConfigFileApplicationListener:
-
public void onApplicationEvent(ApplicationEvent event) {
-
//从此处可以看到当事件为ApplicationEnvironmentPreparedEvent时,执行onApplicationEnvironmentPreparedEvent方法
-
if (event instanceof ApplicationEnvironmentPreparedEvent) {
-
onApplicationEnvironmentPreparedEvent(
-
(ApplicationEnvironmentPreparedEvent) event);
-
}
-
if (event instanceof ApplicationPreparedEvent) {
-
onApplicationPreparedEvent(event);
-
}
-
}
onApplicationEnvironmentPreparedEvent
-
private void onApplicationEnvironmentPreparedEvent(
-
ApplicationEnvironmentPreparedEvent event) {
-
//此处通过SpringFactoriesLoader加载EnvironmentPostProcessor所有扩展
-
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
-
//因为此监听器同样是EnvironmentPostProcessor的扩展实例,所以在此处将自己加入集合
-
postProcessors.add(this);
-
AnnotationAwareOrderComparator.sort(postProcessors);
-
//遍历所有的EnvironmentPostProcessor扩展调用postProcessEnvironment
-
//当然我们跟踪是application.properties所以主要查看当前实例的postProcessEnvironment方法
-
for (EnvironmentPostProcessor postProcessor : postProcessors) {
-
postProcessor.postProcessEnvironment(event.getEnvironment(),
-
event.getSpringApplication());
-
}
-
}
postProcessEnvironment
-
@Override
-
public void postProcessEnvironment(ConfigurableEnvironment environment,
-
SpringApplication application) {
-
//此处添加配置信息到environment实例中,此方法完成后就将application.properties加载到环境信息中
-
addPropertySources(environment, application.getResourceLoader());
-
configureIgnoreBeanInfo(environment);
-
bindToSpringApplication(environment, application);
-
}
addPropertySources
-
protected void addPropertySources(ConfigurableEnvironment environment,
-
ResourceLoader resourceLoader) {
-
//这里先添加一个Random名称的资源到环境信息中
-
RandomValuePropertySource.addToEnvironment(environment);
-
//通过Loader加载application.properties并将信息存入环境信息中
-
new Loader(environment, resourceLoader).load();
-
}
load
-
public void load() {
-
//创建一个资源加载器,spring boot默认支持PropertiesPropertySourceLoader,YamlPropertySourceLoader两种配置文件的加载
-
this.propertiesLoader = new PropertySourcesLoader();
-
this.activatedProfiles = false;
-
//加载配置profile信息,默认为default
-
..........此处省略
-
while (!this.profiles.isEmpty()) {
-
Profile profile = this.profiles.poll();
-
//遍历所有查询路径,默认路径有:classpath:/,classpath:/config/,file:./,file:./config/
-
for (String location : getSearchLocations()) {
-
//这里不仅仅是加载application.properties,当搜索路径不是以/结束,默认认为是文件名已存在的路径
-
if (!location.endsWith("/")) {
-
// location is a filename already, so don't search for more
-
// filenames
-
load(location, null, profile);
-
}
-
else {
-
//遍历要加载的文件名集合,默认为application
-
for (String name : getSearchNames()) {
-
load(location, name, profile);
-
}
-
}
-
}
-
this.processedProfiles.add(profile);
-
}
-
//将加载完成的配置信息全部保存到环境信息*享
-
addConfigurationProperties(this.propertiesLoader.getPropertySources());
-
}
load
-
private void load(String location, String name, Profile profile) {
-
//此处根据profile组装加载的文件名称以及资源所放置的组信息
-
String group = "profile=" + (profile == null ? "" : profile);
-
if (!StringUtils.hasText(name)) {
-
// Try to load directly from the location
-
loadIntoGroup(group, location, profile);
-
}
-
else {
-
// Also try the profile-specific section (if any) of the normal file
-
loadIntoGroup(group, location + name + "." + ext, profile);
-
}
-
}
-
}
loadIntoGroup
-
private PropertySource<?> doLoadIntoGroup(String identifier, String location,
-
Profile profile) throws IOException {
-
Resource resource = this.resourceLoader.getResource(location);
-
PropertySource<?> propertySource = null;
-
if (resource != null && resource.exists()) {
-
String name = "applicationConfig: [" + location + "]";
-
String group = "applicationConfig: [" + identifier + "]";
-
//资源加载核心方法,此处有两个实现,当后缀为,xml或者properties调用PropertiesPropertySourceLoader
-
//当后缀为yml或者yaml时,调用YamlPropertySourceLoader
-
propertySource = this.propertiesLoader.load(resource,
-
}
-
return propertySource;
-
}
- PropertiesPropertySourceLoader:
-
@Override
-
public PropertySource<?> load(String name, Resource resource, String profile)
-
throws IOException {
-
if (profile == null) {
-
//此处调用PropertiesLoaderUtils工具类加载本地文件
-
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
-
if (!properties.isEmpty()) {
-
return new PropertiesPropertySource(name, properties);
-
}
-
}
-
return null;
-
}
到此application.properties就真正的加载并共享到环境信息中,供系统其它地方调用