@ConfigurationProperties 的属性注入是在 initializeBean() 的时候处理的,即 populateBean() 之后,由 ConfigurationPropertiesBindingPostProcessor 处理的,最终会委托给 Environment 来处理(参看:SpringBoot 配置项解析)。
而 @Value 的属性注入是在 populateBean() 的时候处理的,是当作依赖进行注入的。最后是通过 embeddedValueResolvers(StringValueResoulver) 解析的,它非常强大,支持 占位符 ${} 和 spEL(#{})
StringValueResolver、EmbeddedValueResolver
EmbeddedValueResolver implements StringValueResolver。EmbeddedValueResolver 非常强大,它是 spring 配置解析的核心。先解析占位符 ${},再解析 spEL(#{})
1 public class EmbeddedValueResolver implements StringValueResolver { 2 private final BeanExpressionContext exprContext; 3 private final BeanExpressionResolver exprResolver; 4 5 public EmbeddedValueResolver(ConfigurableBeanFactory beanFactory) { 6 this.exprContext = new BeanExpressionContext(beanFactory, null); 7 this.exprResolver = beanFactory.getBeanExpressionResolver(); // 实现类为:StandardBeanExpressionResolver 8 } 9 10 @Override 11 public String resolveStringValue(String strVal) { 12 // 1. 解析占位符${} 13 String value = this.exprContext.getBeanFactory().resolveEmbeddedValue(strVal); 14 if (this.exprResolver != null && value != null) { 15 // 2. 解析 spEL: #{} 16 Object evaluated = this.exprResolver.evaluate(value, this.exprContext); 17 value = (evaluated != null ? evaluated.toString() : null); 18 } 19 return value; 20 } 21 22 }
配置写法小技巧:
可以使用嵌套写法 和 默认值。举例:
${server.error.path:${error.path:/error}}
问题思考:
在使用 disconf 托管配置文件时,会配置 org.springframework.context.support.PropertySourcesPlaceholderConfigurer 或者 com.baidu.disconf.client.addons.properties.ReloadingPropertyPlaceholderConfigurer,被托管的配置都会纳入 spring 管理。如果是这样的话,我们在使用 SpringBoot @ConfigurationProperties 形式的配置时,是不是就可以不用写 @PropertySource("classpath:redis.properties") 来指定配置文件了,这样该多方便啊。那确实可以这么干吗?
分析:
通过测试发现是不可以的,因为 @ConfigurationProperties 最终委托给 Environment 来处理,而 PropertySourcesPlaceholderConfigurer 和 ReloadingPropertyPlaceholderConfigurer 都不受 Environment 控制,所以是不支持的。
而 @Value 却是可以注入 disconf 托管的配置的,因为 @Value 注入的配置会通过 StringValueResoulver 来解析,而 PropertySourcesPlaceholderConfigurer 和 ReloadingPropertyPlaceholderConfigurer 管理的配置,最终会间接通过 StringValueResolver 来解析