Browse Source
Rework the ConfigurationPropertySources and related adapter classes to help with performance. The ConfigurationPropertySources class now only monitors for updates when `.attach` is used. The `.get` methods now return the adapted version, but no longer checks to see if sources have been added or removed on each call. This commit also fixes a few caching issues and makes both the `PropertyMapper` implementations true static singletons. See gh-9000pull/9145/head
25 changed files with 639 additions and 436 deletions
@ -0,0 +1,160 @@
@@ -0,0 +1,160 @@
|
||||
/* |
||||
* Copyright 2012-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.context.properties.source; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Iterator; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
import java.util.stream.Stream; |
||||
import java.util.stream.StreamSupport; |
||||
|
||||
import org.springframework.core.env.ConfigurableEnvironment; |
||||
import org.springframework.core.env.MutablePropertySources; |
||||
import org.springframework.core.env.PropertySource; |
||||
import org.springframework.core.env.PropertySource.StubPropertySource; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ObjectUtils; |
||||
|
||||
/** |
||||
* Adapter to convert Spring's {@link MutablePropertySources} to |
||||
* {@link ConfigurationPropertySource ConfigurationPropertySources}. |
||||
* |
||||
* @author Phillip Webb |
||||
*/ |
||||
class SpringConfigurationPropertySources |
||||
implements Iterable<ConfigurationPropertySource> { |
||||
|
||||
private final MutablePropertySources sources; |
||||
|
||||
private volatile PropertySourcesKey lastKey; |
||||
|
||||
private volatile List<ConfigurationPropertySource> adaptedSources; |
||||
|
||||
SpringConfigurationPropertySources(MutablePropertySources sources) { |
||||
Assert.notNull(sources, "Sources must not be null"); |
||||
this.sources = sources; |
||||
} |
||||
|
||||
@Override |
||||
public Iterator<ConfigurationPropertySource> iterator() { |
||||
checkForChanges(); |
||||
return this.adaptedSources.iterator(); |
||||
} |
||||
|
||||
private void checkForChanges() { |
||||
PropertySourcesKey lastKey = this.lastKey; |
||||
PropertySourcesKey currentKey = new PropertySourcesKey(this.sources); |
||||
if (!currentKey.equals(lastKey)) { |
||||
onChange(this.sources); |
||||
this.lastKey = currentKey; |
||||
} |
||||
} |
||||
|
||||
private void onChange(MutablePropertySources sources) { |
||||
this.adaptedSources = streamPropertySources(sources) |
||||
.map(SpringConfigurationPropertySource::from) |
||||
.collect(Collectors.toList()); |
||||
} |
||||
|
||||
private Stream<PropertySource<?>> streamPropertySources( |
||||
Iterable<PropertySource<?>> sources) { |
||||
return StreamSupport.stream(sources.spliterator(), false).flatMap(this::flatten) |
||||
.filter(this::isIncluded); |
||||
} |
||||
|
||||
private Stream<PropertySource<?>> flatten(PropertySource<?> source) { |
||||
if (source.getSource() instanceof ConfigurableEnvironment) { |
||||
return streamPropertySources( |
||||
((ConfigurableEnvironment) source.getSource()).getPropertySources()); |
||||
} |
||||
return Stream.of(source); |
||||
} |
||||
|
||||
private boolean isIncluded(PropertySource<?> source) { |
||||
return !(source instanceof StubPropertySource) |
||||
&& !(source instanceof ConfigurationPropertySourcesPropertySource); |
||||
} |
||||
|
||||
private static class PropertySourcesKey { |
||||
|
||||
private final List<PropertySourceKey> keys = new ArrayList<>(); |
||||
|
||||
PropertySourcesKey(MutablePropertySources sources) { |
||||
sources.forEach(this::addKey); |
||||
} |
||||
|
||||
private void addKey(PropertySource<?> source) { |
||||
this.keys.add(new PropertySourceKey(source)); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return this.keys.hashCode(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) { |
||||
return true; |
||||
} |
||||
if (obj == null || getClass() != obj.getClass()) { |
||||
return false; |
||||
} |
||||
return this.keys.equals(((PropertySourcesKey) obj).keys); |
||||
} |
||||
|
||||
} |
||||
|
||||
private static class PropertySourceKey { |
||||
|
||||
private final String name; |
||||
|
||||
private final Class<?> type; |
||||
|
||||
PropertySourceKey(PropertySource<?> source) { |
||||
this.name = source.getName(); |
||||
this.type = source.getClass(); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
final int prime = 31; |
||||
int result = 1; |
||||
result = prime * result + ObjectUtils.nullSafeHashCode(this.name); |
||||
result = prime * result + ObjectUtils.nullSafeHashCode(this.type); |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) { |
||||
return true; |
||||
} |
||||
if (obj == null || getClass() != obj.getClass()) { |
||||
return false; |
||||
} |
||||
PropertySourceKey other = (PropertySourceKey) obj; |
||||
boolean result = true; |
||||
result = result && ObjectUtils.nullSafeEquals(this.name, other.name); |
||||
result = result && ObjectUtils.nullSafeEquals(this.type, other.type); |
||||
return result; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
/* |
||||
* Copyright 2012-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.context.properties.source; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Iterator; |
||||
|
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.rules.ExpectedException; |
||||
|
||||
import org.springframework.core.env.Environment; |
||||
import org.springframework.core.env.MapPropertySource; |
||||
import org.springframework.core.env.MutablePropertySources; |
||||
import org.springframework.core.env.PropertySource; |
||||
import org.springframework.core.env.StandardEnvironment; |
||||
import org.springframework.core.env.SystemEnvironmentPropertySource; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link SpringConfigurationPropertySources}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Madhura Bhave |
||||
*/ |
||||
public class SpringConfigurationPropertySourcesTests { |
||||
|
||||
@Rule |
||||
public ExpectedException thrown = ExpectedException.none(); |
||||
|
||||
@Test |
||||
public void createWhenPropertySourcesIsNullShouldThrowException() throws Exception { |
||||
this.thrown.expect(IllegalArgumentException.class); |
||||
this.thrown.expectMessage("Sources must not be null"); |
||||
new SpringConfigurationPropertySources(null); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldAdaptPropertySource() throws Exception { |
||||
MutablePropertySources sources = new MutablePropertySources(); |
||||
sources.addFirst(new MapPropertySource("test", |
||||
Collections.<String, Object>singletonMap("a", "b"))); |
||||
Iterator<ConfigurationPropertySource> iterator = new SpringConfigurationPropertySources( |
||||
sources).iterator(); |
||||
ConfigurationPropertyName name = ConfigurationPropertyName.of("a"); |
||||
assertThat(iterator.next().getConfigurationProperty(name).getValue()) |
||||
.isEqualTo("b"); |
||||
assertThat(iterator.hasNext()).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldAdaptSystemEnvironmentPropertySource() throws Exception { |
||||
MutablePropertySources sources = new MutablePropertySources(); |
||||
sources.addLast(new SystemEnvironmentPropertySource("system", |
||||
Collections.<String, Object>singletonMap("SERVER_PORT", "1234"))); |
||||
Iterator<ConfigurationPropertySource> iterator = new SpringConfigurationPropertySources( |
||||
sources).iterator(); |
||||
ConfigurationPropertyName name = ConfigurationPropertyName.of("server.port"); |
||||
assertThat(iterator.next().getConfigurationProperty(name).getValue()) |
||||
.isEqualTo("1234"); |
||||
assertThat(iterator.hasNext()).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldAdaptMultiplePropertySources() throws Exception { |
||||
MutablePropertySources sources = new MutablePropertySources(); |
||||
sources.addLast(new SystemEnvironmentPropertySource("system", |
||||
Collections.<String, Object>singletonMap("SERVER_PORT", "1234"))); |
||||
sources.addLast(new MapPropertySource("test1", |
||||
Collections.<String, Object>singletonMap("server.po-rt", "4567"))); |
||||
sources.addLast(new MapPropertySource("test2", |
||||
Collections.<String, Object>singletonMap("a", "b"))); |
||||
Iterator<ConfigurationPropertySource> iterator = new SpringConfigurationPropertySources( |
||||
sources).iterator(); |
||||
ConfigurationPropertyName name = ConfigurationPropertyName.of("server.port"); |
||||
assertThat(iterator.next().getConfigurationProperty(name).getValue()) |
||||
.isEqualTo("1234"); |
||||
assertThat(iterator.next().getConfigurationProperty(name).getValue()) |
||||
.isEqualTo("4567"); |
||||
assertThat(iterator.next() |
||||
.getConfigurationProperty(ConfigurationPropertyName.of("a")).getValue()) |
||||
.isEqualTo("b"); |
||||
assertThat(iterator.hasNext()).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldFlattenEnvironment() throws Exception { |
||||
StandardEnvironment environment = new StandardEnvironment(); |
||||
environment.getPropertySources().addFirst(new MapPropertySource("foo", |
||||
Collections.<String, Object>singletonMap("foo", "bar"))); |
||||
environment.getPropertySources().addFirst(new MapPropertySource("far", |
||||
Collections.<String, Object>singletonMap("far", "far"))); |
||||
MutablePropertySources sources = new MutablePropertySources(); |
||||
sources.addFirst(new PropertySource<Environment>("env", environment) { |
||||
|
||||
@Override |
||||
public String getProperty(String key) { |
||||
return this.source.getProperty(key); |
||||
} |
||||
|
||||
}); |
||||
sources.addLast(new MapPropertySource("baz", |
||||
Collections.<String, Object>singletonMap("baz", "barf"))); |
||||
SpringConfigurationPropertySources configurationSources = new SpringConfigurationPropertySources( |
||||
sources); |
||||
assertThat(configurationSources.iterator()).hasSize(5); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldTrackChanges() throws Exception { |
||||
MutablePropertySources sources = new MutablePropertySources(); |
||||
sources.addLast(new MapPropertySource("test1", |
||||
Collections.<String, Object>singletonMap("a", "b"))); |
||||
assertThat(new SpringConfigurationPropertySources(sources).iterator()).hasSize(1); |
||||
sources.addLast(new MapPropertySource("test2", |
||||
Collections.<String, Object>singletonMap("b", "c"))); |
||||
assertThat(new SpringConfigurationPropertySources(sources).iterator()).hasSize(2); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue