mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Don't mix collection values from different sources
Update PropertySourcesPropertyValues so that collection values are only added from a single PropertySource. Prior to this commit, given the following: PropertySource-A list[0]=x PropertySource-B list[0]=y list[1]=z PropertySourcesPropertyValues would take `x` from A and `z` from B, resulting in a binding of `[x,z]`. The updated code now returns the more logical `[x]`. Fixes gh-2611
This commit is contained in:
parent
16a1bd0483
commit
86d5c19259
@ -21,6 +21,8 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.springframework.beans.MutablePropertyValues;
|
import org.springframework.beans.MutablePropertyValues;
|
||||||
import org.springframework.beans.PropertyValue;
|
import org.springframework.beans.PropertyValue;
|
||||||
@ -39,17 +41,22 @@ import org.springframework.validation.DataBinder;
|
|||||||
* used with the latter.
|
* used with the latter.
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Phillip Webb
|
||||||
*/
|
*/
|
||||||
public class PropertySourcesPropertyValues implements PropertyValues {
|
public class PropertySourcesPropertyValues implements PropertyValues {
|
||||||
|
|
||||||
private final Map<String, PropertyValue> propertyValues = new LinkedHashMap<String, PropertyValue>();
|
|
||||||
|
|
||||||
private final PropertySources propertySources;
|
|
||||||
|
|
||||||
private static final Collection<String> PATTERN_MATCHED_PROPERTY_SOURCES = Arrays
|
private static final Collection<String> PATTERN_MATCHED_PROPERTY_SOURCES = Arrays
|
||||||
.asList(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
|
.asList(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
|
||||||
StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
|
StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
|
||||||
|
|
||||||
|
private static final Pattern COLLECTION_PROPERTY = Pattern.compile("\\[(\\d+)\\]");
|
||||||
|
|
||||||
|
private final PropertySources propertySources;
|
||||||
|
|
||||||
|
private final Map<String, PropertyValue> propertyValues = new LinkedHashMap<String, PropertyValue>();
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<String, PropertySource<?>> collectionOwners = new ConcurrentHashMap<String, PropertySource<?>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new PropertyValues from the given PropertySources
|
* Create a new PropertyValues from the given PropertySources
|
||||||
* @param propertySources a PropertySources instance
|
* @param propertySources a PropertySources instance
|
||||||
@ -122,10 +129,7 @@ public class PropertySourcesPropertyValues implements PropertyValues {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Object value = getEnumerableProperty(source, resolver, propertyName);
|
Object value = getEnumerableProperty(source, resolver, propertyName);
|
||||||
if (!this.propertyValues.containsKey(propertyName)) {
|
putIfAbsent(propertyName, value, source);
|
||||||
this.propertyValues.put(propertyName, new PropertyValue(propertyName,
|
|
||||||
value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,9 +170,7 @@ public class PropertySourcesPropertyValues implements PropertyValues {
|
|||||||
if (value == null) {
|
if (value == null) {
|
||||||
value = source.getProperty(propertyName.toUpperCase());
|
value = source.getProperty(propertyName.toUpperCase());
|
||||||
}
|
}
|
||||||
if (value != null && !this.propertyValues.containsKey(propertyName)) {
|
if (putIfAbsent(propertyName, value, source) != null) {
|
||||||
this.propertyValues.put(propertyName, new PropertyValue(propertyName,
|
|
||||||
value));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,8 +190,22 @@ public class PropertySourcesPropertyValues implements PropertyValues {
|
|||||||
}
|
}
|
||||||
for (PropertySource<?> source : this.propertySources) {
|
for (PropertySource<?> source : this.propertySources) {
|
||||||
Object value = source.getProperty(propertyName);
|
Object value = source.getProperty(propertyName);
|
||||||
if (value != null) {
|
propertyValue = putIfAbsent(propertyName, value, source);
|
||||||
propertyValue = new PropertyValue(propertyName, value);
|
if (propertyValue != null) {
|
||||||
|
return propertyValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PropertyValue putIfAbsent(String propertyName, Object value,
|
||||||
|
PropertySource<?> source) {
|
||||||
|
if (value != null && !this.propertyValues.containsKey(propertyName)) {
|
||||||
|
PropertySource<?> collectionOwner = this.collectionOwners.putIfAbsent(
|
||||||
|
COLLECTION_PROPERTY.matcher(propertyName).replaceAll("[]"), source);
|
||||||
|
if (collectionOwner == null || collectionOwner == source) {
|
||||||
|
this.collectionOwners.get(this.collectionOwners);
|
||||||
|
PropertyValue propertyValue = new PropertyValue(propertyName, value);
|
||||||
this.propertyValues.put(propertyName, propertyValue);
|
this.propertyValues.put(propertyName, propertyValue);
|
||||||
return propertyValue;
|
return propertyValue;
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,11 @@
|
|||||||
package org.springframework.boot.bind;
|
package org.springframework.boot.bind;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -31,12 +33,15 @@ import org.springframework.core.env.MutablePropertySources;
|
|||||||
import org.springframework.core.env.PropertySource;
|
import org.springframework.core.env.PropertySource;
|
||||||
import org.springframework.validation.DataBinder;
|
import org.springframework.validation.DataBinder;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link PropertySourcesPropertyValues}.
|
* Tests for {@link PropertySourcesPropertyValues}.
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Phillip Webb
|
||||||
*/
|
*/
|
||||||
public class PropertySourcesPropertyValuesTests {
|
public class PropertySourcesPropertyValuesTests {
|
||||||
|
|
||||||
@ -192,7 +197,35 @@ public class PropertySourcesPropertyValuesTests {
|
|||||||
assertEquals(null, target.getName());
|
assertEquals(null, target.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCollectionProperty() throws Exception {
|
||||||
|
ListBean target = new ListBean();
|
||||||
|
DataBinder binder = new DataBinder(target);
|
||||||
|
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||||
|
map.put("list[0]", "v0");
|
||||||
|
map.put("list[1]", "v1");
|
||||||
|
this.propertySources.addFirst(new MapPropertySource("values", map));
|
||||||
|
binder.bind(new PropertySourcesPropertyValues(this.propertySources));
|
||||||
|
assertThat(target.getList(), equalTo(Arrays.asList("v0", "v1")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFirstCollectionPropertyWins() throws Exception {
|
||||||
|
ListBean target = new ListBean();
|
||||||
|
DataBinder binder = new DataBinder(target);
|
||||||
|
Map<String, Object> first = new LinkedHashMap<String, Object>();
|
||||||
|
first.put("list[0]", "f0");
|
||||||
|
Map<String, Object> second = new LinkedHashMap<String, Object>();
|
||||||
|
second.put("list[0]", "s0");
|
||||||
|
second.put("list[1]", "s1");
|
||||||
|
this.propertySources.addFirst(new MapPropertySource("s", second));
|
||||||
|
this.propertySources.addFirst(new MapPropertySource("f", first));
|
||||||
|
binder.bind(new PropertySourcesPropertyValues(this.propertySources));
|
||||||
|
assertThat(target.getList(), equalTo(Collections.singletonList("f0")));
|
||||||
|
}
|
||||||
|
|
||||||
public static class TestBean {
|
public static class TestBean {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -205,6 +238,7 @@ public class PropertySourcesPropertyValuesTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class FooBean {
|
public static class FooBean {
|
||||||
|
|
||||||
private String foo;
|
private String foo;
|
||||||
|
|
||||||
public String getFoo() {
|
public String getFoo() {
|
||||||
@ -214,6 +248,20 @@ public class PropertySourcesPropertyValuesTests {
|
|||||||
public void setFoo(String foo) {
|
public void setFoo(String foo) {
|
||||||
this.foo = foo;
|
this.foo = foo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ListBean {
|
||||||
|
|
||||||
|
private List<String> list = new ArrayList<String>();
|
||||||
|
|
||||||
|
public List<String> getList() {
|
||||||
|
return this.list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setList(List<String> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user