在上一篇文件中介绍了springboot加载配置文件的方式,即从"./config;./;classpath:/config;classpath:/“等4个地方加载配置文件,配置文件的属性按顺序保存在environment对象的propertysource对象中,当我们读取配置信息时即从propertysource列表中依次查找直到找到为止。
举个例子,我们的应用在”./"和“classpath:/”中分别有一个application-dev.yml的配置文件,在“./”路径下的配置文件有属性“aa.bb= 1”,在"classpath:./“路径下的配置文件有属性"aa.bb=2”。那么当我们调用environment.getProperty(“aa.bb”)时,由于系统会先在“./”路径下的配置文件的配置文件中找到该值,因此系统将不在继续往下查到,因此aa.bb的值为1。
这样看的话好像没有什么问题,但是当aa.bb不在是当个string类型的值而是一个map对象的时候,可就不是那么回事了。
举个例子,“classpath:/”路径下的application-dev.yml配置文件中存在如下配置:
aa.bb={a=1;b=1;c=1},在“./”路径下的配置文件中存在如下配置:aa.bb={},那么系统中“aa.bb”是什么呢,通过定义一个bean可以进行测试:
@Bean
@ConfigurationProperties(prefix = "aa")
public aaObj aaObj(){
return new aaObj();
}
如上aaObj只有一个属性:Map bb;
通过测试我们可以发现aaObj.bb = {a=1;b=1;c=1}
这个问题当时困扰了我一两个小时,后来因为有其他工作就直接把classpath下的配置文件删了才算绕过去,今天正好有时间了正好来研究下这个问题。
一开始我打算在程序中通过Map map = environment.getProperty(“aa.bb”,Map.class)来测试,结果发现获取的值为null,经过调试发现,配置文件中的aa.bb={a=1;b=1;c=1}被系统拆解成了“aa.bb.a =1;aa.bb.b=1;aa.bb.c=1”共三个配置项environment.getProperty需要完全equals才算匹配上。那么@ConfigurationProperties(prefix = “aa”)
注解又是如何完成map类型的属性注入的呢,具体的代码写在MapBinder.bindEntries里:
public void bindEntries(ConfigurationPropertySource source, Map<Object, Object> map) {
if (source instanceof IterableConfigurationPropertySource) {
for (ConfigurationPropertyName name : (IterableConfigurationPropertySource) source) {
Bindable<?> valueBindable = getValueBindable(name);
ConfigurationPropertyName entryName = getEntryName(source, name);
Object key = getContext().getConverter().convert(getKeyName(entryName), this.keyType);
map.computeIfAbsent(key, (k) -> this.elementBinder.bind(entryName, valueBindable));
}
}
}
打断点调试source对象,可以看到他就是environment的sourceProperty,但是不同的是这个source多了一个filter,filter属性为“aa.bb”,然后再ConfigurationPropertyName name : (IterableConfigurationPropertySource) source 中遍历出来的属性就全部是aa.bb有关的。
通过上面的分析我们可以知道,当我们在配置文件中注入map类型的属性时,需要注意springboot对这类配置的处理。总的来说,我觉得还是删除那些不打算使用的配置文件比较靠谱呀,不然的话真的会变成一场灾难。