SpringBoot是怎样加载配置文件的?
2019-11-18杂谈搜奇网45°c
A+ A-媒介
本文针对版本2.2.0.RELEASE
来剖析SpringBoot的设置处置惩罚源码,经由过程检察SpringBoot的源码来弄清楚一些罕见的题目比方:
- SpringBoot从那里最先加载设置文件?
- SpringBoot从哪些处所加载设置文件?
- SpringBoot是怎样支撑
yaml
和properties
范例的设置文件? - 假如要支撑
json
设置应当怎样做? - SpringBoot的设置优先级是怎样的?
- placeholder是怎样被剖析的?
带着我们的题目一同去看一下SpringBoot设置相干的源代码,找出题目的答案。
SpringBoot从那里最先加载设置文件?
SpringBoot加载设置文件的进口是由ApplicationEnvironmentPreparedEvent
事宜进入的,SpringBoot会在SpringApplication的组织函数中经由过程spring.factories
文件猎取ApplicationListener的实例类:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
...
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
...
}
spring.factories
中有一个ConfigFileApplicationListener
类,它会监听ApplicationEnvironmentPreparedEvent
然后再加载设置文件 :
# Application Listeners
org.springframework.context.ApplicationListener= org.springframework.boot.context.config.ConfigFileApplicationListener
...
有了事宜和事宜处置惩罚的类后,再找出发送事宜的处所,就可以够搞清楚SpringBoot是怎样加载设置文件的了,SpringBoot在启动之前先初始化好SpringApplicationRunListeners这个类,它会完成SpringApplicationRunListener接口然后对事宜举行转发:
class SpringApplicationRunListeners {
private final Log log;
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
...
}
猎取SpringApplicationRunListeners的代码以下:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
一样也会去加载spring.factories
文件,该文件有一个EventPublishingRunListener类,该类的作用就是SpringBoot的事宜转换成ApplicationEvent发送出去。
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
小结
- SpringBoot会将事宜转换成ApplicationEvent再分发
- SpringBoot是经由过程监听
ApplicationEnvironmentPreparedEvent
事宜来加载设置文件的 - ConfigFileApplicationListener是处置惩罚设置文件的重要类
SpringBoot从哪些处所加载设置文件?
上面已剖析到ConfigFileApplicationListener是处置惩罚设置文件的重要类,然后进一步的检察SpringBoot是从哪些地点加载设置文件,进入ConfigFileApplicationListener类后会有两个默许的常量:
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
private static final String DEFAULT_NAMES = "application";
首先在没有任何设置的状况下,会从DEFAULT_SEARCH_LOCATIONS
常量列出来的位置中加载文件名为DEFAULT_NAMES(.properties或yml)
的文件,默许位置包含:
- classpath根目次(classpath:/)
- classpath内里的config文件目次(classpath:/config/)
- 顺序运转目次(file:./)
- 顺序运转目次下的config目次(file:./config/)
上面说的是没有分外设置的状况,SpringBoot充足天真能够指定设置文件搜刮途径、设置文件名,在ConfigFileApplicationListener类中有个getSearchLocations
要领,它重要担任猎取设置搜刮目次:
private Set<String> getSearchLocations() {
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
return getSearchLocations(CONFIG_LOCATION_PROPERTY);
}
Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
locations.addAll(
asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
return locations;
}
它的操纵步骤大抵以下:
- 搜检是不是有
spring.config.location
属性,假如存在则直接运用它的值 - 从
spring.config.additional-location
属性中猎取搜刮途径 - 将默许搜刮途径添加到搜刮鸠合
这里就可以够肯定SpringBoot设置的搜刮途径有两种状况:假如设置了spring.config.location
则直接运用,不然运用spring.config.additional-location
的属性值 + 默许搜刮途径。
SpringBoot是怎样支撑yaml
和properties
范例的设置文件?
SpringBoot的设置支撑properties
和yaml
文件,SpringBoot是怎样剖析这两种文件的呢,继承剖析ConfigFileApplicationListener
这个类,内里有个子类叫Loader
加载设置文件重要的事情就是由这货担任,然则直接读取properties
和yaml
并转换成PropertySource
照样由内里的PropertySourceLoader
担任:
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
...
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
组织Loader
对象的时刻就会先加载PropertySourceLoader,加载体式格局照样从spring.factories
中读取:
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
个中设置了两个PropertySourceLoader的完成类:
- PropertiesPropertySourceLoader
- YamlPropertySourceLoader
看名字就晓得是离别担任properties
和yaml
的啦。
假如要支撑json
设置应当怎样做?
假如不喜欢properties
和yaml
这两种花样,想要定义json
做为设置笔墨花样能够直接定义json范例的PropertySourceLoader:
public class JSONPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] {"json"};
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
if(resource == null || !resource.exists()){
return Collections.emptyList();
}
Map<String, Object> configs = JSON.parseObject(resource.getInputStream(), Map.class);
return Collections.singletonList(
new MapPropertySource(name, configs)
);
}
}
然后在resources
目次内里竖立个META-INF
,再添加个spring.factories
内里的内容以下:
org.springframework.boot.env.PropertySourceLoader=\
com.csbaic.arch.spring.env.loader.JSONPropertySourceLoader
末了在resources
目次内里建个application.json
的设置文件 :
{
"spring.application.name": "JSONConfig"
}
一般启动SpringBoot猎取spring.applicaiton.name
的设置的值就是JSONConfig
:
2019-11-02 14:50:17.730 INFO 55275 --- [ main] c.c.a.spring.env.SpringEnvApplication : JSONConfig
SpringBoot的设置优先级是怎样的?
SpringBoot中有个PropertySource
接口,特地用来保留属性罕见的完成类有:
- CommandLinePropertySource
- MapPropertySource
- SystemEnvironmentPropertySource
- ....
别的为了集合治理PropertySource
还笼统出一个PropertySources
接口,PropertySources就一个完成类叫:MutablePropertySources,它将一切的PropertySource都安排在一个名叫propertySourceList
鸠合中,同时供应一些修正操纵要领:
public void addFirst(PropertySource<?> propertySource) {}
public void addLast(PropertySource<?> propertySource) {}
public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource) {}
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) {}
public int precedenceOf(PropertySource<?> propertySource) { }
public PropertySource<?> remove(String name) {}
public void replace(String name, PropertySource<?> propertySource) {}
一切的PropertySource都保留在propertySourceList中,越小的索引优先级越高,所以假如想要掩盖属性只需保证优化级够高就行。
placeholder是怎样被剖析的?
继承剖析ConfigFileApplicationListener
的Loader
子类,在组织时还会建立一个PropertySourcesPlaceholdersResolver
,placeholder的剖析都由它来完成:
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
}
剖析PropertySourcesPlaceholdersResolver发明,真正完成剖析是由PropertyPlaceholderHelper
完成,PropertySourcesPlaceholdersResolver 在组织的时刻就会建立一个PropertyPlaceholderHelper
public PropertySourcesPlaceholdersResolver(Iterable<PropertySource<?>> sources, PropertyPlaceholderHelper helper) {
this.sources = sources;
this.helper = (helper != null) ? helper : new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true);
}
PropertySourcesPlaceholdersResolver 在建立 PropertyPlaceholderHelper 的时刻会通报三个参数:前缀、后缀、默许值支解符,离别由以下三个常量示意:
public static final String PLACEHOLDER_PREFIX = "${";
public static final String PLACEHOLDER_SUFFIX = "}";
public static final String VALUE_SEPARATOR = ":";
如许 PropertyPlaceholderHelper 在剖析placeholder时就可以晓得以什么花样来剖析比方:${spring.application.name}这个placeholder就会被剖析成属性值。
总结
SpringBoot的设置异常天真设置能够来自文件、环境变量、JVM体系属性、设置中间等等,SpringBoot经由过程
PropertySource
和PropertySources
完成属性优先级、CRUD的一致治理,为开发者供应一致的设置笼统。
《架构文摘》天天一篇架构范畴重磅好文,触及一线互联网公司运用架构(高可用、高性 能、高稳固)、大数据、机械进修等各个热点范畴。