Spring Boot多PropertySourcesPlaceholderConfigurer致@Value默认值优先问题
问题回顾
使用@Value("${placeholder-config-path:default-value}")注入属性时,始终加载默认值;去掉默认值后能正常读取配置。排查发现存在两个PropertySourcesPlaceholderConfigurer实例,分别来自业务应用上下文和Spring Cloud的bootstrap上下文,均由PropertyPlaceholderAutoConfiguration自动生成。
解决方案
1. 禁用Bootstrap上下文的自动配置
在bootstrap.properties或bootstrap.yml中添加配置,阻止Bootstrap上下文生成多余的PropertySourcesPlaceholderConfigurer:
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
这样仅保留业务应用上下文的实例,避免多实例冲突。
2. 显式定义单一的PropertySourcesPlaceholderConfigurer
通过自定义配置类创建唯一的实例,覆盖自动配置的Bean(自动配置类中使用了@ConditionalOnMissingBean,会优先使用显式定义的Bean):
@Configuration public class CustomPlaceholderConfig { @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); configurer.setIgnoreUnresolvablePlaceholders(true); return configurer; } }
3. 升级Spring Cloud版本(根源解决)
如果使用的是旧版Spring Cloud(依赖Bootstrap上下文),升级到2021.0.x(Jubilee)及以上版本。这些版本默认弃用Bootstrap上下文,改用spring.config.import方式加载配置中心配置,从根本上避免多上下文导致的多实例问题。
原因解析
当存在多个PropertySourcesPlaceholderConfigurer实例时,先执行的实例会优先处理@Value占位符:
- 带默认值的情况下,若该实例找不到配置值,结合
ignoreUnresolvablePlaceholders=true,会直接使用默认值填充,后续实例不再处理该占位符; - 不带默认值时,先执行的实例找不到值会忽略,后续能读取到配置的实例会继续解析,因此能拿到正确值。
两个实例的产生是因为Spring Cloud的Bootstrap上下文和业务应用上下文各自触发了PropertyPlaceholderAutoConfiguration的自动配置,导致重复实例化。
内容的提问来源于stack exchange,提问作者adarsh hegde

