parent
8a3b0cd221
commit
be5039041c
|
@ -53,18 +53,22 @@ class MapBinder extends AggregateBinder<Map<Object, Object>> {
|
||||||
@Override
|
@Override
|
||||||
protected Object bindAggregate(ConfigurationPropertyName name, Bindable<?> target,
|
protected Object bindAggregate(ConfigurationPropertyName name, Bindable<?> target,
|
||||||
AggregateElementBinder elementBinder) {
|
AggregateElementBinder elementBinder) {
|
||||||
Map<Object, Object> map = CollectionFactory
|
|
||||||
.createMap((target.getValue() != null) ? Map.class : target.getType().resolve(Object.class), 0);
|
|
||||||
Bindable<?> resolvedTarget = resolveTarget(target);
|
Bindable<?> resolvedTarget = resolveTarget(target);
|
||||||
boolean hasDescendants = hasDescendants(name);
|
boolean hasDescendants = hasDescendants(name);
|
||||||
|
if (!hasDescendants && !ConfigurationPropertyName.EMPTY.equals(name)) {
|
||||||
for (ConfigurationPropertySource source : getContext().getSources()) {
|
for (ConfigurationPropertySource source : getContext().getSources()) {
|
||||||
if (!ConfigurationPropertyName.EMPTY.equals(name)) {
|
|
||||||
ConfigurationProperty property = source.getConfigurationProperty(name);
|
ConfigurationProperty property = source.getConfigurationProperty(name);
|
||||||
if (property != null && !hasDescendants) {
|
if (property != null) {
|
||||||
getContext().setConfigurationProperty(property);
|
getContext().setConfigurationProperty(property);
|
||||||
Object result = getContext().getPlaceholdersResolver().resolvePlaceholders(property.getValue());
|
Object result = getContext().getPlaceholdersResolver().resolvePlaceholders(property.getValue());
|
||||||
return getContext().getConverter().convert(result, target);
|
return getContext().getConverter().convert(result, target);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<Object, Object> map = CollectionFactory
|
||||||
|
.createMap((target.getValue() != null) ? Map.class : target.getType().resolve(Object.class), 0);
|
||||||
|
for (ConfigurationPropertySource source : getContext().getSources()) {
|
||||||
|
if (!ConfigurationPropertyName.EMPTY.equals(name)) {
|
||||||
source = source.filter(name::isAncestorOf);
|
source = source.filter(name::isAncestorOf);
|
||||||
}
|
}
|
||||||
new EntryBinder(name, resolvedTarget, elementBinder).bindEntries(source, map);
|
new EntryBinder(name, resolvedTarget, elementBinder).bindEntries(source, map);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.boot.context.properties.bind;
|
package org.springframework.boot.context.properties.bind;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.util.AbstractMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -24,6 +25,7 @@ import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -33,6 +35,7 @@ import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.bind.BinderTests.ExampleEnum;
|
import org.springframework.boot.context.properties.bind.BinderTests.ExampleEnum;
|
||||||
import org.springframework.boot.context.properties.bind.BinderTests.JavaBean;
|
import org.springframework.boot.context.properties.bind.BinderTests.JavaBean;
|
||||||
|
import org.springframework.boot.context.properties.bind.MapBinderTests.CustomMapWithoutDefaultCtor.CustomMap;
|
||||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
||||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||||
|
@ -78,7 +81,7 @@ class MapBinderTests {
|
||||||
|
|
||||||
private final List<ConfigurationPropertySource> sources = new ArrayList<>();
|
private final List<ConfigurationPropertySource> sources = new ArrayList<>();
|
||||||
|
|
||||||
private Binder binder = new Binder(this.sources);
|
private final Binder binder = new Binder(this.sources);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void bindToMapShouldReturnPopulatedMap() {
|
void bindToMapShouldReturnPopulatedMap() {
|
||||||
|
@ -315,15 +318,13 @@ class MapBinderTests {
|
||||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, "foo=boo");
|
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, "foo=boo");
|
||||||
MockConfigurationPropertySource source = new MockConfigurationPropertySource("foo.aaa.bbb.ccc", "baz-${foo}");
|
MockConfigurationPropertySource source = new MockConfigurationPropertySource("foo.aaa.bbb.ccc", "baz-${foo}");
|
||||||
this.sources.add(source);
|
this.sources.add(source);
|
||||||
this.binder = new Binder(this.sources, new PropertySourcesPlaceholdersResolver(environment));
|
Binder binder = new Binder(this.sources, new PropertySourcesPlaceholdersResolver(environment));
|
||||||
Map<String, ExampleEnum> result = this.binder.bind("foo", Bindable.mapOf(String.class, ExampleEnum.class))
|
Map<String, ExampleEnum> result = binder.bind("foo", Bindable.mapOf(String.class, ExampleEnum.class)).get();
|
||||||
.get();
|
|
||||||
assertThat(result).containsEntry("aaa.bbb.ccc", ExampleEnum.BAZ_BOO);
|
assertThat(result).containsEntry("aaa.bbb.ccc", ExampleEnum.BAZ_BOO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void bindToMapWithNoPropertiesShouldReturnUnbound() {
|
void bindToMapWithNoPropertiesShouldReturnUnbound() {
|
||||||
this.binder = new Binder(this.sources);
|
|
||||||
BindResult<Map<String, ExampleEnum>> result = this.binder.bind("foo",
|
BindResult<Map<String, ExampleEnum>> result = this.binder.bind("foo",
|
||||||
Bindable.mapOf(String.class, ExampleEnum.class));
|
Bindable.mapOf(String.class, ExampleEnum.class));
|
||||||
assertThat(result.isBound()).isFalse();
|
assertThat(result.isBound()).isFalse();
|
||||||
|
@ -624,6 +625,18 @@ class MapBinderTests {
|
||||||
assertThat(map).containsKey("bcd");
|
assertThat(map).containsKey("bcd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void bindToCustomMapWithoutCtorAndConverterShouldResolve() {
|
||||||
|
DefaultConversionService conversionService = new DefaultConversionService();
|
||||||
|
conversionService.addConverter(new CustomMapConverter());
|
||||||
|
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
|
||||||
|
source.put("foo.custom-map", "value");
|
||||||
|
this.sources.add(source);
|
||||||
|
Binder binder = new Binder(this.sources, null, conversionService, null);
|
||||||
|
CustomMapWithoutDefaultCtor result = binder.bind("foo", Bindable.of(CustomMapWithoutDefaultCtor.class)).get();
|
||||||
|
assertThat(result.getCustomMap().getSource()).isEqualTo("value");
|
||||||
|
}
|
||||||
|
|
||||||
private <K, V> Bindable<Map<K, V>> getMapBindable(Class<K> keyGeneric, ResolvableType valueType) {
|
private <K, V> Bindable<Map<K, V>> getMapBindable(Class<K> keyGeneric, ResolvableType valueType) {
|
||||||
ResolvableType keyType = ResolvableType.forClass(keyGeneric);
|
ResolvableType keyType = ResolvableType.forClass(keyGeneric);
|
||||||
return Bindable.of(ResolvableType.forClassWithGenerics(Map.class, keyType, valueType));
|
return Bindable.of(ResolvableType.forClassWithGenerics(Map.class, keyType, valueType));
|
||||||
|
@ -761,6 +774,48 @@ class MapBinderTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class CustomMapWithoutDefaultCtor {
|
||||||
|
|
||||||
|
private final CustomMap customMap;
|
||||||
|
|
||||||
|
CustomMapWithoutDefaultCtor(CustomMap customMap) {
|
||||||
|
this.customMap = customMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomMap getCustomMap() {
|
||||||
|
return this.customMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class CustomMap extends AbstractMap<String, Object> {
|
||||||
|
|
||||||
|
private final String source;
|
||||||
|
|
||||||
|
CustomMap(String source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<String, Object>> entrySet() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getSource() {
|
||||||
|
return this.source;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CustomMapConverter implements Converter<String, CustomMap> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomMap convert(String source) {
|
||||||
|
return new CustomMap(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static final class InvocationArgument<T> implements Answer<T> {
|
private static final class InvocationArgument<T> implements Answer<T> {
|
||||||
|
|
||||||
private final int index;
|
private final int index;
|
||||||
|
|
Loading…
Reference in New Issue