!!!以下内容为作者原创,首发于掘金平台。未经原作者同意与许可,任何人、任何组织不得以任何形式转载。原创不易,如果对您的问题提供了些许帮助,希望得到您的点赞支持。
0.碰到的问题
记录下最近公司项目开发中碰到的一个问题:@value 注解只读默认值,读取不到配置文件中的值,记录一下解决的心路历程,供大家参考。
因为开发需要在application.properties的中新加了一个参数spring.redis.used。实际使用中通过下面这样来引入:
@Value("${spring.redis.used}")
这都很常见,但是带来一个问题:
代码写完一提交,并没有提交本地配置文件,很多公司小伙伴不知道改动了启动配置文件,造成项目启动就直接报错了:
虽然可以让大家都更新上这个启动参数,但是多少有点不“完美”。遂通过百度想找更好点的解决方法,于是找到下面方法。通过这种方式,如果启动配置文件中没有,就会取冒号后面的默认值。
@Value("${spring.redis.used:false}")
经过测试能达到目的:没有配置参数,也能正常启动项目,而且是取到默认值。问题到这一步,似乎解决了。
但是反复测试发现另一个新的问题:
取不到配置项中的参数了,即使配置了,读出来依旧是默认值
1.问题分析
1.1找到出问题出处
经过层层寻找,终于找到 spring 源码中解析@Value的方法: AbstractBeanFactory中的resolveEmbeddedValue
@Override
@Nullable
public String resolveEmbeddedValue(@Nullable String value) {
if (value == null) {
return null;
}
String result = value;
for (StringValueResolver resolver : this.embeddedValueResolvers) {
result = resolver.resolveStringValue(result);
if (result == null) {
return null;
}
}
return result;
}
稍微分析一下这段代码。就是通过循环项目中配置的value解析器,处理所有@value 注解,获取到解析后的值。然后发现下面这句就是问题所在:
result = resolver.resolveStringValue(result);
每个解析器将 result 拿来处理,处理完后将处理结果又放回到result。如果这个解析器没有读取出来,而是读了默认值,那后面的所有解析器其实一直对这个默认值在解析处理,所以引发我们的项目不管有没有配置这个参数都读不到真实的值。
当时就很不解了,公司项目并没有配置过额外的解析器啊,按理就只有一个 springboot 自动的配置。所以就调试到这块,拿到真实的 embeddedValueResolvers 值看个究竟。
1.2为什么会出现多个解析器
下面放debug截图
如果是用过 ureport 报表的同学应该能看明白了,这两个解析器都是因为我们项目中引入了一个开源报表工具 Ureport,而这个开源报表里面自动配置了这两个解析器
对这个开源报表的配置就不进入分析了,总之就是这个工具引入导致的。
那这时下一个疑问就出来了,为什么这两个Ureport 解析器在前面,而 springboot 默认的解析器位于最后呢?
1.3 Resolver 的加载顺序
下面是这3个解析器的 Order属性:
第1个解析器:Order 100
第2个解析器:Order 2147483647
第3个解析器(springboot自带):Order 2147483647
下面两个order 值应该是没有配置过 order 属性而自带的,第1个order 应该是在初始化时有配置文件指定的,于是经过一阵搜索找到下面这段 Ureport 的配置代码
package com.bstek.ureport;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
public class UReportPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
public UReportPropertyPlaceholderConfigurer() {
this.setIgnoreUnresolvablePlaceholders(true);
this.setOrder(100);
}
}
原来如此,那我们的解决方法就和它一样,把springboot 默认的解析器 order 顺序配置到 Ureport 前就好了
1.4 重新配置 spring resolver 顺序
在项目中加入一段自定义配置:
@Configuration
public class PropertySourcesPlaceholderConfigurer extends org.springframework.context.support.PropertySourcesPlaceholderConfigurer {
PropertySourcesPlaceholderConfigurer(){
setIgnoreUnresolvablePlaceholders(true);
setOrder(99);
}
}
测试下来正常解决该问题。
2.事后思考
虽然我们的问题得到解决,但是始终觉得 spring 在解析那块的处理似乎有问题。这样如果有多个配置文件,默认解析器没有读取到配置文件,是不是也会造成后面自定义的解析器也得不到真实值?
我也怀疑是不是我们引入的 spring 版本过旧了,而新的版本是不是已经修复此问题?
于是到 spring 的github 里面去看了最新的代码依旧如此。
随后又去issues 中用下面关键词搜了一下:
is:issue is:open resolveEmbeddedValue
果然,出现2个哥们提出和我们几乎一样的问题,下面是链接:感兴趣的小伙伴们可以串过去看看
# Allow next resolver to resolve when current resolver return null #26247
都是2020年提出来的了,看了下状态的标签是:waiting-for-triage,也不明白官方是啥意思
立个 flag ,以后想起来时再去看看官方会不会接收 issue中的建议,重新修改这段处理代码。