Skip to content

Commit a0a6a42

Browse files
committed
Add a property to fail fast for unresolvable placeholders in @ConfigurationProperties
Introduce the `spring.configurationproperties.ignore-unresolvable-placeholders` property to control whether an exception should be thrown when a property placeholder `${...}` cannot be resolved. Signed-off-by: Dmytro Nosan <[email protected]>
1 parent 6bb432d commit a0a6a42

File tree

3 files changed

+47
-2
lines changed

3 files changed

+47
-2
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBinder.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,8 +49,11 @@
4949
import org.springframework.context.ConfigurableApplicationContext;
5050
import org.springframework.core.annotation.MergedAnnotations;
5151
import org.springframework.core.convert.ConversionService;
52+
import org.springframework.core.env.Environment;
5253
import org.springframework.core.env.PropertySources;
5354
import org.springframework.util.Assert;
55+
import org.springframework.util.PropertyPlaceholderHelper;
56+
import org.springframework.util.SystemPropertyUtils;
5457
import org.springframework.validation.Errors;
5558
import org.springframework.validation.Validator;
5659
import org.springframework.validation.annotation.Validated;
@@ -68,6 +71,8 @@ class ConfigurationPropertiesBinder {
6871

6972
private static final String VALIDATOR_BEAN_NAME = EnableConfigurationProperties.VALIDATOR_BEAN_NAME;
7073

74+
private static final String IGNORE_UNRESOLVABLE_PLACEHOLDER_PROPERTY = "spring.configurationproperties.ignore-unresolvable-placeholders";
75+
7176
private final ApplicationContext applicationContext;
7277

7378
private final PropertySources propertySources;
@@ -191,7 +196,16 @@ private Iterable<ConfigurationPropertySource> getConfigurationPropertySources()
191196
}
192197

193198
private PropertySourcesPlaceholdersResolver getPropertySourcesPlaceholdersResolver() {
194-
return new PropertySourcesPlaceholdersResolver(this.propertySources);
199+
return new PropertySourcesPlaceholdersResolver(this.propertySources, getPropertyPlaceholderHelper());
200+
}
201+
202+
private PropertyPlaceholderHelper getPropertyPlaceholderHelper() {
203+
Environment environment = this.applicationContext.getEnvironment();
204+
boolean ignoreUnresolvablePlaceholders = environment.getProperty(IGNORE_UNRESOLVABLE_PLACEHOLDER_PROPERTY,
205+
Boolean.class, true);
206+
return new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
207+
SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR,
208+
SystemPropertyUtils.ESCAPE_CHARACTER, ignoreUnresolvablePlaceholders);
195209
}
196210

197211
private List<ConversionService> getConversionServices() {

spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json

+7
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,13 @@
475475
"description": "Config file name.",
476476
"defaultValue": "application"
477477
},
478+
{
479+
"name": "spring.configurationproperties.ignore-unresolvable-placeholders",
480+
"type": "java.lang.Boolean",
481+
"sourceType": "org.springframework.boot.context.properties.ConfigurationPropertiesBinder",
482+
"description": "Whether unresolvable placeholders should be ignored or trigger an exception during the binding of configuration properties",
483+
"defaultValue": "true"
484+
},
478485
{
479486
"name": "spring.jpa.defer-datasource-initialization",
480487
"type": "java.lang.Boolean",

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java

+24
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,30 @@ void loadWhenHasIgnoreUnknownFieldsFalseAndUnknownFieldsShouldFail() {
194194
.withCauseInstanceOf(BindException.class);
195195
}
196196

197+
@Test
198+
void loadWhenIgnoreUnresolvablePlaceholdersSetsToFalseShouldFail() {
199+
assertThatExceptionOfType(ConfigurationPropertiesBindException.class)
200+
.isThrownBy(() -> load(BasicConfiguration.class, "name=${FOO}",
201+
"spring.configurationproperties.ignore-unresolvable-placeholders=false"))
202+
.withCauseInstanceOf(BindException.class)
203+
.withStackTraceContaining("Could not resolve placeholder 'FOO'");
204+
}
205+
206+
@Test
207+
void loadWhenIgnoreUnresolvablePlaceholdersSetsToTrueShouldNotFail() {
208+
load(BasicConfiguration.class, "name=${FOO}",
209+
"spring.configurationproperties.ignore-unresolvable-placeholders=true");
210+
BasicProperties properties = this.context.getBean(BasicProperties.class);
211+
assertThat(properties.name).isEqualTo("${FOO}");
212+
}
213+
214+
@Test
215+
void loadWhenIgnoreUnresolvablePlaceholdersSetsToTrueByDefaultShouldNotFail() {
216+
load(BasicConfiguration.class, "name=${FOO}");
217+
BasicProperties properties = this.context.getBean(BasicProperties.class);
218+
assertThat(properties.name).isEqualTo("${FOO}");
219+
}
220+
197221
@Test
198222
void givenIgnoreUnknownFieldsFalseAndIgnoreInvalidFieldsTrueWhenThereAreUnknownFieldsThenBindingShouldFail() {
199223
removeSystemProperties();

0 commit comments

Comments
 (0)