Skip to content

Commit 48009bf

Browse files
committed
Allow relaxed property name overrides
Update RelaxedDataBinder to so that property ordering is respected even if relaxed names are used. Prior to this commit a System property named `FOO_BAR` would never get bound to a `fooBar` field if `foo-bar` was defined in application.properties. Fixes gh-3385
1 parent 04dfac1 commit 48009bf

File tree

4 files changed

+80
-12
lines changed

4 files changed

+80
-12
lines changed

spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
import java.util.ArrayList;
2121
import java.util.Collection;
2222
import java.util.Collections;
23+
import java.util.HashSet;
2324
import java.util.LinkedHashMap;
2425
import java.util.LinkedList;
2526
import java.util.List;
2627
import java.util.Map;
28+
import java.util.Set;
2729

2830
import org.springframework.beans.BeanWrapper;
2931
import org.springframework.beans.BeanWrapperImpl;
@@ -139,10 +141,14 @@ private MutablePropertyValues modifyProperties(MutablePropertyValues propertyVal
139141
wrapper.setConversionService(new RelaxedConversionService(getConversionService()));
140142
wrapper.setAutoGrowNestedPaths(true);
141143
List<PropertyValue> sortedValues = new ArrayList<PropertyValue>();
144+
Set<String> modifiedNames = new HashSet<String>();
142145
List<String> sortedNames = getSortedPropertyNames(propertyValues);
143146
for (String name : sortedNames) {
144-
sortedValues.add(modifyProperty(wrapper,
145-
propertyValues.getPropertyValue(name)));
147+
PropertyValue propertyValue = propertyValues.getPropertyValue(name);
148+
PropertyValue modifiedProperty = modifyProperty(wrapper, propertyValue);
149+
if (modifiedNames.add(modifiedProperty.getName())) {
150+
sortedValues.add(modifiedProperty);
151+
}
146152
}
147153
return new MutablePropertyValues(sortedValues);
148154
}

spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java

+10
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ public static class Foo {
139139

140140
private String spring_foo_baz;
141141

142+
private String fooBar;
143+
142144
public String getSpringFooBaz() {
143145
return this.spring_foo_baz;
144146
}
@@ -163,6 +165,14 @@ public void setBar(String bar) {
163165
this.bar = bar;
164166
}
165167

168+
public String getFooBar() {
169+
return this.fooBar;
170+
}
171+
172+
public void setFooBar(String fooBar) {
173+
this.fooBar = fooBar;
174+
}
175+
166176
}
167177

168178
}

spring-boot/src/test/java/org/springframework/boot/bind/RelaxedDataBinderTests.java

+22-10
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,18 @@ public void testBindWithAlias() throws Exception {
525525
assertThat(target.getFoo(), equalTo("b"));
526526
}
527527

528+
@Test
529+
public void testMixed() throws Exception {
530+
// gh-3385
531+
VanillaTarget target = new VanillaTarget();
532+
RelaxedDataBinder binder = getBinder(target, "test");
533+
MutablePropertyValues values = new MutablePropertyValues();
534+
values.add("test.FOO_BAZ", "boo");
535+
values.add("test.foo-baz", "bar");
536+
binder.bind(values);
537+
assertEquals("boo", target.getFooBaz());
538+
}
539+
528540
private void doTestBindCaseInsensitiveEnums(VanillaTarget target) throws Exception {
529541
BindingResult result = bind(target, "bingo: THIS");
530542
assertThat(result.getErrorCount(), equalTo(0));
@@ -555,16 +567,6 @@ private BindingResult bind(Object target, String values) throws Exception {
555567
return bind(target, values, null);
556568
}
557569

558-
private BindingResult bind(DataBinder binder, Object target, String values)
559-
throws Exception {
560-
Properties properties = PropertiesLoaderUtils
561-
.loadProperties(new ByteArrayResource(values.getBytes()));
562-
binder.bind(new MutablePropertyValues(properties));
563-
binder.validate();
564-
565-
return binder.getBindingResult();
566-
}
567-
568570
private BindingResult bind(Object target, String values, String namePrefix)
569571
throws Exception {
570572
return bind(getBinder(target, namePrefix), target, values);
@@ -580,6 +582,16 @@ private RelaxedDataBinder getBinder(Object target, String namePrefix) {
580582
return binder;
581583
}
582584

585+
private BindingResult bind(DataBinder binder, Object target, String values)
586+
throws Exception {
587+
Properties properties = PropertiesLoaderUtils
588+
.loadProperties(new ByteArrayResource(values.getBytes()));
589+
binder.bind(new MutablePropertyValues(properties));
590+
binder.validate();
591+
592+
return binder.getBindingResult();
593+
}
594+
583595
@Documented
584596
@Target({ ElementType.FIELD })
585597
@Retention(RUNTIME)

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

+40
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,29 @@ public void notWritablePropertyException() throws Exception {
236236
this.context.refresh();
237237
}
238238

239+
@Test
240+
public void relaxedPropertyNamesSame() throws Exception {
241+
this.context = new AnnotationConfigApplicationContext();
242+
EnvironmentTestUtils.addEnvironment(this.context, "test.FOO_BAR:test1");
243+
EnvironmentTestUtils.addEnvironment(this.context, "test.FOO_BAR:test2");
244+
this.context.register(RelaxedPropertyNames.class);
245+
this.context.refresh();
246+
assertThat(this.context.getBean(RelaxedPropertyNames.class).getFooBar(),
247+
equalTo("test2"));
248+
}
249+
250+
@Test
251+
public void relaxedPropertyNamesMixed() throws Exception {
252+
// gh-3385
253+
this.context = new AnnotationConfigApplicationContext();
254+
EnvironmentTestUtils.addEnvironment(this.context, "test.foo-bar:test1");
255+
EnvironmentTestUtils.addEnvironment(this.context, "test.FOO_BAR:test2");
256+
this.context.register(RelaxedPropertyNames.class);
257+
this.context.refresh();
258+
assertThat(this.context.getBean(RelaxedPropertyNames.class).getFooBar(),
259+
equalTo("test2"));
260+
}
261+
239262
@Configuration
240263
@EnableConfigurationProperties
241264
public static class TestConfigurationWithValidatingSetter {
@@ -468,6 +491,23 @@ public static class ConfigurationPropertiesWithFactoryBean {
468491

469492
}
470493

494+
@Configuration
495+
@EnableConfigurationProperties
496+
@ConfigurationProperties(prefix = "test")
497+
public static class RelaxedPropertyNames {
498+
499+
private String fooBar;
500+
501+
public String getFooBar() {
502+
return this.fooBar;
503+
}
504+
505+
public void setFooBar(String fooBar) {
506+
this.fooBar = fooBar;
507+
}
508+
509+
}
510+
471511
@SuppressWarnings("rawtypes")
472512
// Must be a raw type
473513
static class FactoryBeanTester implements FactoryBean, InitializingBean {

0 commit comments

Comments
 (0)