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.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.beans.BeanWrapper;
|
import org.springframework.beans.BeanWrapper;
|
||||||
import org.springframework.beans.BeanWrapperImpl;
|
import org.springframework.beans.BeanWrapperImpl;
|
||||||
|
|
@ -139,10 +141,14 @@ public class RelaxedDataBinder extends DataBinder {
|
||||||
wrapper.setConversionService(new RelaxedConversionService(getConversionService()));
|
wrapper.setConversionService(new RelaxedConversionService(getConversionService()));
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
wrapper.setAutoGrowNestedPaths(true);
|
||||||
List<PropertyValue> sortedValues = new ArrayList<PropertyValue>();
|
List<PropertyValue> sortedValues = new ArrayList<PropertyValue>();
|
||||||
|
Set<String> modifiedNames = new HashSet<String>();
|
||||||
List<String> sortedNames = getSortedPropertyNames(propertyValues);
|
List<String> sortedNames = getSortedPropertyNames(propertyValues);
|
||||||
for (String name : sortedNames) {
|
for (String name : sortedNames) {
|
||||||
sortedValues.add(modifyProperty(wrapper,
|
PropertyValue propertyValue = propertyValues.getPropertyValue(name);
|
||||||
propertyValues.getPropertyValue(name)));
|
PropertyValue modifiedProperty = modifyProperty(wrapper, propertyValue);
|
||||||
|
if (modifiedNames.add(modifiedProperty.getName())) {
|
||||||
|
sortedValues.add(modifiedProperty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new MutablePropertyValues(sortedValues);
|
return new MutablePropertyValues(sortedValues);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,8 @@ public class PropertiesConfigurationFactoryTests {
|
||||||
|
|
||||||
private String spring_foo_baz;
|
private String spring_foo_baz;
|
||||||
|
|
||||||
|
private String fooBar;
|
||||||
|
|
||||||
public String getSpringFooBaz() {
|
public String getSpringFooBaz() {
|
||||||
return this.spring_foo_baz;
|
return this.spring_foo_baz;
|
||||||
}
|
}
|
||||||
|
|
@ -163,6 +165,14 @@ public class PropertiesConfigurationFactoryTests {
|
||||||
this.bar = bar;
|
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"));
|
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 {
|
private void doTestBindCaseInsensitiveEnums(VanillaTarget target) throws Exception {
|
||||||
BindingResult result = bind(target, "bingo: THIS");
|
BindingResult result = bind(target, "bingo: THIS");
|
||||||
assertThat(result.getErrorCount(), equalTo(0));
|
assertThat(result.getErrorCount(), equalTo(0));
|
||||||
|
|
@ -555,16 +567,6 @@ public class RelaxedDataBinderTests {
|
||||||
return bind(target, values, null);
|
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)
|
private BindingResult bind(Object target, String values, String namePrefix)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
return bind(getBinder(target, namePrefix), target, values);
|
return bind(getBinder(target, namePrefix), target, values);
|
||||||
|
|
@ -580,6 +582,16 @@ public class RelaxedDataBinderTests {
|
||||||
return binder;
|
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
|
@Documented
|
||||||
@Target({ ElementType.FIELD })
|
@Target({ ElementType.FIELD })
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,29 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
|
||||||
this.context.refresh();
|
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
|
@Configuration
|
||||||
@EnableConfigurationProperties
|
@EnableConfigurationProperties
|
||||||
public static class TestConfigurationWithValidatingSetter {
|
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")
|
@SuppressWarnings("rawtypes")
|
||||||
// Must be a raw type
|
// Must be a raw type
|
||||||
static class FactoryBeanTester implements FactoryBean, InitializingBean {
|
static class FactoryBeanTester implements FactoryBean, InitializingBean {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue