Browse Source

Change property mapper to use array returns

Update the `PropertyMapper` interface to return arrays rather than
Lists. Since implementations are package-private it's possible for us
to control how they are used and it helps to save a little memory.

Fixes gh-11411
pull/11365/merge
Phillip Webb 8 years ago
parent
commit
5f10c82284
  1. 29
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/DefaultPropertyMapper.java
  2. 8
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/PropertyMapper.java
  3. 26
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java
  4. 33
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java
  5. 37
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SystemEnvironmentPropertyMapper.java
  6. 5
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/AbstractPropertyMapperTests.java
  7. 6
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SystemEnvironmentPropertyMapperTests.java
  8. 20
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/TestPropertyMapper.java

29
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/DefaultPropertyMapper.java

@ -16,9 +16,6 @@
package org.springframework.boot.context.properties.source; package org.springframework.boot.context.properties.source;
import java.util.Collections;
import java.util.List;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
/** /**
@ -43,55 +40,53 @@ final class DefaultPropertyMapper implements PropertyMapper {
} }
@Override @Override
public List<PropertyMapping> map( public PropertyMapping[] map(ConfigurationPropertyName configurationPropertyName) {
ConfigurationPropertyName configurationPropertyName) {
// Use a local copy in case another thread changes things // Use a local copy in case another thread changes things
LastMapping<ConfigurationPropertyName> last = this.lastMappedConfigurationPropertyName; LastMapping<ConfigurationPropertyName> last = this.lastMappedConfigurationPropertyName;
if (last != null && last.isFrom(configurationPropertyName)) { if (last != null && last.isFrom(configurationPropertyName)) {
return last.getMapping(); return last.getMapping();
} }
String convertedName = configurationPropertyName.toString(); String convertedName = configurationPropertyName.toString();
List<PropertyMapping> mapping = Collections.singletonList( PropertyMapping[] mapping = {
new PropertyMapping(convertedName, configurationPropertyName)); new PropertyMapping(convertedName, configurationPropertyName) };
this.lastMappedConfigurationPropertyName = new LastMapping<>( this.lastMappedConfigurationPropertyName = new LastMapping<>(
configurationPropertyName, mapping); configurationPropertyName, mapping);
return mapping; return mapping;
} }
@Override @Override
public List<PropertyMapping> map(String propertySourceName) { public PropertyMapping[] map(String propertySourceName) {
// Use a local copy in case another thread changes things // Use a local copy in case another thread changes things
LastMapping<String> last = this.lastMappedPropertyName; LastMapping<String> last = this.lastMappedPropertyName;
if (last != null && last.isFrom(propertySourceName)) { if (last != null && last.isFrom(propertySourceName)) {
return last.getMapping(); return last.getMapping();
} }
List<PropertyMapping> mapping = tryMap(propertySourceName); PropertyMapping[] mapping = tryMap(propertySourceName);
this.lastMappedPropertyName = new LastMapping<>(propertySourceName, mapping); this.lastMappedPropertyName = new LastMapping<>(propertySourceName, mapping);
return mapping; return mapping;
} }
private List<PropertyMapping> tryMap(String propertySourceName) { private PropertyMapping[] tryMap(String propertySourceName) {
try { try {
ConfigurationPropertyName convertedName = ConfigurationPropertyName ConfigurationPropertyName convertedName = ConfigurationPropertyName
.adapt(propertySourceName, '.'); .adapt(propertySourceName, '.');
if (!convertedName.isEmpty()) { if (!convertedName.isEmpty()) {
PropertyMapping o = new PropertyMapping(propertySourceName, return new PropertyMapping[] {
convertedName); new PropertyMapping(propertySourceName, convertedName) };
return Collections.singletonList(o);
} }
} }
catch (Exception ex) { catch (Exception ex) {
} }
return Collections.emptyList(); return NO_MAPPINGS;
} }
private static class LastMapping<T> { private static class LastMapping<T> {
private final T from; private final T from;
private final List<PropertyMapping> mapping; private final PropertyMapping[] mapping;
LastMapping(T from, List<PropertyMapping> mapping) { LastMapping(T from, PropertyMapping[] mapping) {
this.from = from; this.from = from;
this.mapping = mapping; this.mapping = mapping;
} }
@ -100,7 +95,7 @@ final class DefaultPropertyMapper implements PropertyMapper {
return ObjectUtils.nullSafeEquals(from, this.from); return ObjectUtils.nullSafeEquals(from, this.from);
} }
public List<PropertyMapping> getMapping() { public PropertyMapping[] getMapping() {
return this.mapping; return this.mapping;
} }

8
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/PropertyMapper.java

@ -16,8 +16,6 @@
package org.springframework.boot.context.properties.source; package org.springframework.boot.context.properties.source;
import java.util.List;
import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
@ -40,19 +38,21 @@ import org.springframework.core.env.PropertySource;
*/ */
interface PropertyMapper { interface PropertyMapper {
PropertyMapping[] NO_MAPPINGS = {};
/** /**
* Provide mappings from a {@link ConfigurationPropertySource} * Provide mappings from a {@link ConfigurationPropertySource}
* {@link ConfigurationPropertyName}. * {@link ConfigurationPropertyName}.
* @param configurationPropertyName the name to map * @param configurationPropertyName the name to map
* @return a stream of mappings or {@code Stream#empty()} * @return a stream of mappings or {@code Stream#empty()}
*/ */
List<PropertyMapping> map(ConfigurationPropertyName configurationPropertyName); PropertyMapping[] map(ConfigurationPropertyName configurationPropertyName);
/** /**
* Provide mappings from a {@link PropertySource} property name. * Provide mappings from a {@link PropertySource} property name.
* @param propertySourceName the name to map * @param propertySourceName the name to map
* @return a stream of mappings or {@code Stream#empty()} * @return a stream of mappings or {@code Stream#empty()}
*/ */
List<PropertyMapping> map(String propertySourceName); PropertyMapping[] map(String propertySourceName);
} }

26
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java

@ -16,10 +16,7 @@
package org.springframework.boot.context.properties.source; package org.springframework.boot.context.properties.source;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Random; import java.util.Random;
import java.util.function.Function; import java.util.function.Function;
@ -85,7 +82,7 @@ class SpringConfigurationPropertySource implements ConfigurationPropertySource {
@Override @Override
public ConfigurationProperty getConfigurationProperty( public ConfigurationProperty getConfigurationProperty(
ConfigurationPropertyName name) { ConfigurationPropertyName name) {
List<PropertyMapping> mappings = getMapper().map(name); PropertyMapping[] mappings = getMapper().map(name);
return find(mappings, name); return find(mappings, name);
} }
@ -100,10 +97,17 @@ class SpringConfigurationPropertySource implements ConfigurationPropertySource {
return this.propertySource; return this.propertySource;
} }
protected final ConfigurationProperty find(List<PropertyMapping> mappings, protected final ConfigurationProperty find(PropertyMapping[] mappings,
ConfigurationPropertyName name) { ConfigurationPropertyName name) {
return mappings.stream().filter((m) -> m.isApplicable(name)).map(this::find) for (PropertyMapping candidate : mappings) {
.filter(Objects::nonNull).findFirst().orElse(null); if (candidate.isApplicable(name)) {
ConfigurationProperty result = find(candidate);
if (result != null) {
return result;
}
}
}
return null;
} }
private ConfigurationProperty find(PropertyMapping mapping) { private ConfigurationProperty find(PropertyMapping mapping) {
@ -214,23 +218,23 @@ class SpringConfigurationPropertySource implements ConfigurationPropertySource {
} }
@Override @Override
public List<PropertyMapping> map( public PropertyMapping[] map(
ConfigurationPropertyName configurationPropertyName) { ConfigurationPropertyName configurationPropertyName) {
try { try {
return this.mapper.map(configurationPropertyName); return this.mapper.map(configurationPropertyName);
} }
catch (Exception ex) { catch (Exception ex) {
return Collections.emptyList(); return NO_MAPPINGS;
} }
} }
@Override @Override
public List<PropertyMapping> map(String propertySourceName) { public PropertyMapping[] map(String propertySourceName) {
try { try {
return this.mapper.map(propertySourceName); return this.mapper.map(propertySourceName);
} }
catch (Exception ex) { catch (Exception ex) {
return Collections.emptyList(); return NO_MAPPINGS;
} }
} }

33
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java

@ -71,7 +71,7 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
ConfigurationProperty configurationProperty = super.getConfigurationProperty( ConfigurationProperty configurationProperty = super.getConfigurationProperty(
name); name);
if (configurationProperty == null) { if (configurationProperty == null) {
configurationProperty = find(getPropertyMappings(), name); configurationProperty = find(getPropertyMappings(getCache()), name);
} }
return configurationProperty; return configurationProperty;
} }
@ -98,8 +98,8 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
if (names != null) { if (names != null) {
return names; return names;
} }
List<PropertyMapping> mappings = getPropertyMappings(); PropertyMapping[] mappings = getPropertyMappings(cache);
names = new ArrayList<>(mappings.size()); names = new ArrayList<>(mappings.length);
for (PropertyMapping mapping : mappings) { for (PropertyMapping mapping : mappings) {
names.add(mapping.getConfigurationPropertyName()); names.add(mapping.getConfigurationPropertyName());
} }
@ -110,22 +110,23 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
return names; return names;
} }
private List<PropertyMapping> getPropertyMappings() { private PropertyMapping[] getPropertyMappings(Cache cache) {
Cache cache = getCache(); PropertyMapping[] result = (cache != null ? cache.getMappings() : null);
List<PropertyMapping> mappings = (cache != null ? cache.getMappings() : null); if (result != null) {
if (mappings != null) { return result;
return mappings;
} }
String[] names = getPropertySource().getPropertyNames(); String[] names = getPropertySource().getPropertyNames();
mappings = new ArrayList<>(names.length); List<PropertyMapping> mappings = new ArrayList<>(names.length * 2);
for (String name : names) { for (String name : names) {
mappings.addAll(getMapper().map(name)); for (PropertyMapping mapping : getMapper().map(name)) {
mappings.add(mapping);
}
} }
mappings = Collections.unmodifiableList(mappings); result = mappings.toArray(new PropertyMapping[mappings.size()]);
if (cache != null) { if (cache != null) {
cache.setMappings(mappings); cache.setMappings(result);
} }
return mappings; return result;
} }
private Cache getCache() { private Cache getCache() {
@ -157,7 +158,7 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
private List<ConfigurationPropertyName> names; private List<ConfigurationPropertyName> names;
private List<PropertyMapping> mappings; private PropertyMapping[] mappings;
public List<ConfigurationPropertyName> getNames() { public List<ConfigurationPropertyName> getNames() {
return this.names; return this.names;
@ -167,11 +168,11 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
this.names = names; this.names = names;
} }
public List<PropertyMapping> getMappings() { public PropertyMapping[] getMappings() {
return this.mappings; return this.mappings;
} }
public void setMappings(List<PropertyMapping> mappings) { public void setMappings(PropertyMapping[] mappings) {
this.mappings = mappings; this.mappings = mappings;
} }

37
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SystemEnvironmentPropertyMapper.java

@ -16,11 +16,6 @@
package org.springframework.boot.context.properties.source; package org.springframework.boot.context.properties.source;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName.Form; import org.springframework.boot.context.properties.source.ConfigurationPropertyName.Form;
@ -31,10 +26,6 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyN
* "{@code .}". For example, "{@code SERVER_PORT}" is mapped to "{@code server.port}". In * "{@code .}". For example, "{@code SERVER_PORT}" is mapped to "{@code server.port}". In
* addition, numeric elements are mapped to indexes (e.g. "{@code HOST_0}" is mapped to * addition, numeric elements are mapped to indexes (e.g. "{@code HOST_0}" is mapped to
* "{@code host[0]}"). * "{@code host[0]}").
* <p>
* List shortcuts (names that end with double underscore) are also supported by this
* mapper. For example, "{@code MY_LIST__=a,b,c}" is mapped to "{@code my.list[0]=a}",
* "{@code my.list[1]=b}", "{@code my.list[2]=c}".
* *
* @author Phillip Webb * @author Phillip Webb
* @author Madhura Bhave * @author Madhura Bhave
@ -45,28 +36,26 @@ final class SystemEnvironmentPropertyMapper implements PropertyMapper {
public static final PropertyMapper INSTANCE = new SystemEnvironmentPropertyMapper(); public static final PropertyMapper INSTANCE = new SystemEnvironmentPropertyMapper();
private SystemEnvironmentPropertyMapper() {
}
@Override @Override
public List<PropertyMapping> map( public PropertyMapping[] map(ConfigurationPropertyName configurationPropertyName) {
ConfigurationPropertyName configurationPropertyName) { String name = convertName(configurationPropertyName);
Set<String> names = new LinkedHashSet<>(); String legacyName = convertLegacyName(configurationPropertyName);
names.add(convertName(configurationPropertyName)); if (name.equals(legacyName)) {
names.add(convertLegacyName(configurationPropertyName)); return new PropertyMapping[] {
List<PropertyMapping> result = new ArrayList<>(); new PropertyMapping(name, configurationPropertyName) };
names.forEach((name) -> result }
.add(new PropertyMapping(name, configurationPropertyName))); return new PropertyMapping[] {
return result; new PropertyMapping(name, configurationPropertyName),
new PropertyMapping(legacyName, configurationPropertyName), };
} }
@Override @Override
public List<PropertyMapping> map(String propertySourceName) { public PropertyMapping[] map(String propertySourceName) {
ConfigurationPropertyName name = convertName(propertySourceName); ConfigurationPropertyName name = convertName(propertySourceName);
if (name == null || name.isEmpty()) { if (name == null || name.isEmpty()) {
return Collections.emptyList(); return NO_MAPPINGS;
} }
return Collections.singletonList(new PropertyMapping(propertySourceName, name)); return new PropertyMapping[] { new PropertyMapping(propertySourceName, name) };
} }
private ConfigurationPropertyName convertName(String propertySourceName) { private ConfigurationPropertyName convertName(String propertySourceName) {

5
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/AbstractPropertyMapperTests.java

@ -16,6 +16,7 @@
package org.springframework.boot.context.properties.source; package org.springframework.boot.context.properties.source;
import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
/** /**
@ -34,7 +35,7 @@ public abstract class AbstractPropertyMapperTests {
} }
protected final Iterator<String> namesFromString(String name, Object value) { protected final Iterator<String> namesFromString(String name, Object value) {
return getMapper().map(name).stream() return Arrays.stream(getMapper().map(name))
.map((mapping) -> mapping.getConfigurationPropertyName().toString()) .map((mapping) -> mapping.getConfigurationPropertyName().toString())
.iterator(); .iterator();
} }
@ -44,7 +45,7 @@ public abstract class AbstractPropertyMapperTests {
} }
protected final Iterator<String> namesFromConfiguration(String name, String value) { protected final Iterator<String> namesFromConfiguration(String name, String value) {
return getMapper().map(ConfigurationPropertyName.of(name)).stream() return Arrays.stream(getMapper().map(ConfigurationPropertyName.of(name)))
.map(PropertyMapping::getPropertySourceName).iterator(); .map(PropertyMapping::getPropertySourceName).iterator();
} }

6
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SystemEnvironmentPropertyMapperTests.java

@ -16,8 +16,6 @@
package org.springframework.boot.context.properties.source; package org.springframework.boot.context.properties.source;
import java.util.List;
import org.junit.Test; import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -61,7 +59,7 @@ public class SystemEnvironmentPropertyMapperTests extends AbstractPropertyMapper
@Test @Test
public void underscoreShouldNotMapToEmptyString() { public void underscoreShouldNotMapToEmptyString() {
List<PropertyMapping> mappings = getMapper().map("_"); PropertyMapping[] mappings = getMapper().map("_");
boolean applicable = false; boolean applicable = false;
for (PropertyMapping mapping : mappings) { for (PropertyMapping mapping : mappings) {
applicable = mapping.isApplicable(ConfigurationPropertyName.of("")); applicable = mapping.isApplicable(ConfigurationPropertyName.of(""));
@ -71,7 +69,7 @@ public class SystemEnvironmentPropertyMapperTests extends AbstractPropertyMapper
@Test @Test
public void underscoreWithWhitespaceShouldNotMapToEmptyString() { public void underscoreWithWhitespaceShouldNotMapToEmptyString() {
List<PropertyMapping> mappings = getMapper().map(" _"); PropertyMapping[] mappings = getMapper().map(" _");
boolean applicable = false; boolean applicable = false;
for (PropertyMapping mapping : mappings) { for (PropertyMapping mapping : mappings) {
applicable = mapping.isApplicable(ConfigurationPropertyName.of("")); applicable = mapping.isApplicable(ConfigurationPropertyName.of(""));

20
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/TestPropertyMapper.java

@ -17,8 +17,6 @@
package org.springframework.boot.context.properties.source; package org.springframework.boot.context.properties.source;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -46,21 +44,17 @@ class TestPropertyMapper implements PropertyMapper {
} }
} }
public void addFromConfigurationProperty(ConfigurationPropertyName from, String to,
Function<Object, Object> extractor) {
this.fromConfig.add(from, new PropertyMapping(to, from, extractor));
}
@Override @Override
public List<PropertyMapping> map(String propertySourceName) { public PropertyMapping[] map(String propertySourceName) {
return this.fromSource.getOrDefault(propertySourceName, Collections.emptyList()); return this.fromSource.getOrDefault(propertySourceName, Collections.emptyList())
.toArray(new PropertyMapping[0]);
} }
@Override @Override
public List<PropertyMapping> map( public PropertyMapping[] map(ConfigurationPropertyName configurationPropertyName) {
ConfigurationPropertyName configurationPropertyName) { return this.fromConfig
return this.fromConfig.getOrDefault(configurationPropertyName, .getOrDefault(configurationPropertyName, Collections.emptyList())
Collections.emptyList()); .toArray(new PropertyMapping[0]);
} }
} }

Loading…
Cancel
Save