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
This commit is contained in:
parent
04dfac1c6c
commit
48009bfebe
|
|
@ -20,10 +20,12 @@ import java.net.InetAddress;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
|
|
@ -139,10 +141,14 @@ public class RelaxedDataBinder extends DataBinder {
|
|||
wrapper.setConversionService(new RelaxedConversionService(getConversionService()));
|
||||
wrapper.setAutoGrowNestedPaths(true);
|
||||
List<PropertyValue> sortedValues = new ArrayList<PropertyValue>();
|
||||
Set<String> modifiedNames = new HashSet<String>();
|
||||
List<String> sortedNames = getSortedPropertyNames(propertyValues);
|
||||
for (String name : sortedNames) {
|
||||
sortedValues.add(modifyProperty(wrapper,
|
||||
propertyValues.getPropertyValue(name)));
|
||||
PropertyValue propertyValue = propertyValues.getPropertyValue(name);
|
||||
PropertyValue modifiedProperty = modifyProperty(wrapper, propertyValue);
|
||||
if (modifiedNames.add(modifiedProperty.getName())) {
|
||||
sortedValues.add(modifiedProperty);
|
||||
}
|
||||
}
|
||||
return new MutablePropertyValues(sortedValues);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@ public class PropertiesConfigurationFactoryTests {
|
|||
|
||||
private String spring_foo_baz;
|
||||
|
||||
private String fooBar;
|
||||
|
||||
public String getSpringFooBaz() {
|
||||
return this.spring_foo_baz;
|
||||
}
|
||||
|
|
@ -163,6 +165,14 @@ public class PropertiesConfigurationFactoryTests {
|
|||
this.bar = bar;
|
||||
}
|
||||
|
||||
public String getFooBar() {
|
||||
return this.fooBar;
|
||||
}
|
||||
|
||||
public void setFooBar(String fooBar) {
|
||||
this.fooBar = fooBar;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -525,6 +525,18 @@ public class RelaxedDataBinderTests {
|
|||
assertThat(target.getFoo(), equalTo("b"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMixed() throws Exception {
|
||||
// gh-3385
|
||||
VanillaTarget target = new VanillaTarget();
|
||||
RelaxedDataBinder binder = getBinder(target, "test");
|
||||
MutablePropertyValues values = new MutablePropertyValues();
|
||||
values.add("test.FOO_BAZ", "boo");
|
||||
values.add("test.foo-baz", "bar");
|
||||
binder.bind(values);
|
||||
assertEquals("boo", target.getFooBaz());
|
||||
}
|
||||
|
||||
private void doTestBindCaseInsensitiveEnums(VanillaTarget target) throws Exception {
|
||||
BindingResult result = bind(target, "bingo: THIS");
|
||||
assertThat(result.getErrorCount(), equalTo(0));
|
||||
|
|
@ -555,16 +567,6 @@ public class RelaxedDataBinderTests {
|
|||
return bind(target, values, null);
|
||||
}
|
||||
|
||||
private BindingResult bind(DataBinder binder, Object target, String values)
|
||||
throws Exception {
|
||||
Properties properties = PropertiesLoaderUtils
|
||||
.loadProperties(new ByteArrayResource(values.getBytes()));
|
||||
binder.bind(new MutablePropertyValues(properties));
|
||||
binder.validate();
|
||||
|
||||
return binder.getBindingResult();
|
||||
}
|
||||
|
||||
private BindingResult bind(Object target, String values, String namePrefix)
|
||||
throws Exception {
|
||||
return bind(getBinder(target, namePrefix), target, values);
|
||||
|
|
@ -580,6 +582,16 @@ public class RelaxedDataBinderTests {
|
|||
return binder;
|
||||
}
|
||||
|
||||
private BindingResult bind(DataBinder binder, Object target, String values)
|
||||
throws Exception {
|
||||
Properties properties = PropertiesLoaderUtils
|
||||
.loadProperties(new ByteArrayResource(values.getBytes()));
|
||||
binder.bind(new MutablePropertyValues(properties));
|
||||
binder.validate();
|
||||
|
||||
return binder.getBindingResult();
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RUNTIME)
|
||||
|
|
|
|||
|
|
@ -236,6 +236,29 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
|
|||
this.context.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void relaxedPropertyNamesSame() throws Exception {
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "test.FOO_BAR:test1");
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "test.FOO_BAR:test2");
|
||||
this.context.register(RelaxedPropertyNames.class);
|
||||
this.context.refresh();
|
||||
assertThat(this.context.getBean(RelaxedPropertyNames.class).getFooBar(),
|
||||
equalTo("test2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void relaxedPropertyNamesMixed() throws Exception {
|
||||
// gh-3385
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "test.foo-bar:test1");
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "test.FOO_BAR:test2");
|
||||
this.context.register(RelaxedPropertyNames.class);
|
||||
this.context.refresh();
|
||||
assertThat(this.context.getBean(RelaxedPropertyNames.class).getFooBar(),
|
||||
equalTo("test2"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public static class TestConfigurationWithValidatingSetter {
|
||||
|
|
@ -468,6 +491,23 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConfigurationProperties(prefix = "test")
|
||||
public static class RelaxedPropertyNames {
|
||||
|
||||
private String fooBar;
|
||||
|
||||
public String getFooBar() {
|
||||
return this.fooBar;
|
||||
}
|
||||
|
||||
public void setFooBar(String fooBar) {
|
||||
this.fooBar = fooBar;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
// Must be a raw type
|
||||
static class FactoryBeanTester implements FactoryBean, InitializingBean {
|
||||
|
|
|
|||
Loading…
Reference in New Issue