parent
8a3b0cd221
commit
be5039041c
|
@ -53,18 +53,22 @@ class MapBinder extends AggregateBinder<Map<Object, Object>> {
|
|||
@Override
|
||||
protected Object bindAggregate(ConfigurationPropertyName name, Bindable<?> target,
|
||||
AggregateElementBinder elementBinder) {
|
||||
Map<Object, Object> map = CollectionFactory
|
||||
.createMap((target.getValue() != null) ? Map.class : target.getType().resolve(Object.class), 0);
|
||||
Bindable<?> resolvedTarget = resolveTarget(target);
|
||||
boolean hasDescendants = hasDescendants(name);
|
||||
for (ConfigurationPropertySource source : getContext().getSources()) {
|
||||
if (!ConfigurationPropertyName.EMPTY.equals(name)) {
|
||||
if (!hasDescendants && !ConfigurationPropertyName.EMPTY.equals(name)) {
|
||||
for (ConfigurationPropertySource source : getContext().getSources()) {
|
||||
ConfigurationProperty property = source.getConfigurationProperty(name);
|
||||
if (property != null && !hasDescendants) {
|
||||
if (property != null) {
|
||||
getContext().setConfigurationProperty(property);
|
||||
Object result = getContext().getPlaceholdersResolver().resolvePlaceholders(property.getValue());
|
||||
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);
|
||||
}
|
||||
new EntryBinder(name, resolvedTarget, elementBinder).bindEntries(source, map);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.context.properties.bind;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -24,6 +25,7 @@ import java.util.LinkedHashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.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.ConfigurationPropertySource;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
|
@ -78,7 +81,7 @@ class MapBinderTests {
|
|||
|
||||
private final List<ConfigurationPropertySource> sources = new ArrayList<>();
|
||||
|
||||
private Binder binder = new Binder(this.sources);
|
||||
private final Binder binder = new Binder(this.sources);
|
||||
|
||||
@Test
|
||||
void bindToMapShouldReturnPopulatedMap() {
|
||||
|
@ -315,15 +318,13 @@ class MapBinderTests {
|
|||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, "foo=boo");
|
||||
MockConfigurationPropertySource source = new MockConfigurationPropertySource("foo.aaa.bbb.ccc", "baz-${foo}");
|
||||
this.sources.add(source);
|
||||
this.binder = new Binder(this.sources, new PropertySourcesPlaceholdersResolver(environment));
|
||||
Map<String, ExampleEnum> result = this.binder.bind("foo", Bindable.mapOf(String.class, ExampleEnum.class))
|
||||
.get();
|
||||
Binder binder = new Binder(this.sources, new PropertySourcesPlaceholdersResolver(environment));
|
||||
Map<String, ExampleEnum> result = binder.bind("foo", Bindable.mapOf(String.class, ExampleEnum.class)).get();
|
||||
assertThat(result).containsEntry("aaa.bbb.ccc", ExampleEnum.BAZ_BOO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindToMapWithNoPropertiesShouldReturnUnbound() {
|
||||
this.binder = new Binder(this.sources);
|
||||
BindResult<Map<String, ExampleEnum>> result = this.binder.bind("foo",
|
||||
Bindable.mapOf(String.class, ExampleEnum.class));
|
||||
assertThat(result.isBound()).isFalse();
|
||||
|
@ -624,6 +625,18 @@ class MapBinderTests {
|
|||
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) {
|
||||
ResolvableType keyType = ResolvableType.forClass(keyGeneric);
|
||||
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 final int index;
|
||||
|
|
Loading…
Reference in New Issue