Merge pull request #16717 from dreis2211

* pr/16717:
  Polish "Optimize CacheKey handling for immutable sources"
  Optimize CacheKey handling for immutable sources

Closes gh-16717
pull/17069/head
Phillip Webb 6 years ago
commit 05ad95548b

@ -25,9 +25,11 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.env.SystemEnvironmentPropertySource; import org.springframework.core.env.SystemEnvironmentPropertySource;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@ -190,6 +192,9 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
private static final class CacheKey { private static final class CacheKey {
private static final CacheKey IMMUTABLE_PROPERTY_SOURCE = new CacheKey(
new Object[0]);
private final Object key; private final Object key;
private CacheKey(Object key) { private CacheKey(Object key) {
@ -197,6 +202,9 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
} }
public CacheKey copy() { public CacheKey copy() {
if (this == IMMUTABLE_PROPERTY_SOURCE) {
return IMMUTABLE_PROPERTY_SOURCE;
}
return new CacheKey(copyKey(this.key)); return new CacheKey(copyKey(this.key));
} }
@ -215,7 +223,8 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
if (obj == null || getClass() != obj.getClass()) { if (obj == null || getClass() != obj.getClass()) {
return false; return false;
} }
return ObjectUtils.nullSafeEquals(this.key, ((CacheKey) obj).key); CacheKey otherCacheKey = (CacheKey) obj;
return ObjectUtils.nullSafeEquals(this.key, otherCacheKey.key);
} }
@Override @Override
@ -224,12 +233,27 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
} }
public static CacheKey get(EnumerablePropertySource<?> source) { public static CacheKey get(EnumerablePropertySource<?> source) {
if (isImmutable(source)) {
return IMMUTABLE_PROPERTY_SOURCE;
}
if (source instanceof MapPropertySource) { if (source instanceof MapPropertySource) {
return new CacheKey(((MapPropertySource) source).getSource().keySet()); MapPropertySource mapPropertySource = (MapPropertySource) source;
return new CacheKey(mapPropertySource.getSource().keySet());
} }
return new CacheKey(source.getPropertyNames()); return new CacheKey(source.getPropertyNames());
} }
private static boolean isImmutable(EnumerablePropertySource<?> source) {
if (source instanceof OriginTrackedMapPropertySource) {
return ((OriginTrackedMapPropertySource) source).isImmutable();
}
if (StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
.equals(source.getName())) {
return source.getSource() == System.getenv();
}
return false;
}
} }
} }

@ -22,6 +22,7 @@ import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup; import org.springframework.boot.origin.OriginLookup;
import org.springframework.boot.origin.OriginTrackedValue; import org.springframework.boot.origin.OriginTrackedValue;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
/** /**
* {@link OriginLookup} backed by a {@link Map} containing {@link OriginTrackedValue * {@link OriginLookup} backed by a {@link Map} containing {@link OriginTrackedValue
@ -35,9 +36,28 @@ import org.springframework.core.env.MapPropertySource;
public final class OriginTrackedMapPropertySource extends MapPropertySource public final class OriginTrackedMapPropertySource extends MapPropertySource
implements OriginLookup<String> { implements OriginLookup<String> {
@SuppressWarnings({ "unchecked", "rawtypes" }) private final boolean immutable;
/**
* Create a new {@link OriginTrackedMapPropertySource} instance.
* @param name the property source name
* @param source the underlying map source
*/
@SuppressWarnings("rawtypes")
public OriginTrackedMapPropertySource(String name, Map source) { public OriginTrackedMapPropertySource(String name, Map source) {
this(name, source, false);
}
/**
* Create a new {@link OriginTrackedMapPropertySource} instance.
* @param name the property source name
* @param source the underlying map source
* @param immutable if the underlying source is immutable and guaranteed not to change
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public OriginTrackedMapPropertySource(String name, Map source, boolean immutable) {
super(name, source); super(name, source);
this.immutable = immutable;
} }
@Override @Override
@ -58,4 +78,13 @@ public final class OriginTrackedMapPropertySource extends MapPropertySource
return null; return null;
} }
/**
* Return {@code true} if this {@link PropertySource} is immutable and has contents
* that will never change.
* @return if the property source is read only
*/
public boolean isImmutable() {
return this.immutable;
}
} }

@ -48,8 +48,8 @@ public class PropertiesPropertySourceLoader implements PropertySourceLoader {
if (properties.isEmpty()) { if (properties.isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
} }
return Collections return Collections.singletonList(new OriginTrackedMapPropertySource(name,
.singletonList(new OriginTrackedMapPropertySource(name, properties)); Collections.unmodifiableMap(properties), true));
} }
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })

@ -55,7 +55,7 @@ public class YamlPropertySourceLoader implements PropertySourceLoader {
for (int i = 0; i < loaded.size(); i++) { for (int i = 0; i < loaded.size(); i++) {
String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : ""; String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber, propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
loaded.get(i))); Collections.unmodifiableMap(loaded.get(i)), true));
} }
return propertySources; return propertySources;
} }

@ -25,6 +25,7 @@ import java.util.Set;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup; import org.springframework.boot.origin.OriginLookup;
import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.EnumerablePropertySource;
@ -160,7 +161,7 @@ public class SpringIterableConfigurationPropertySourceTests {
} }
@Test @Test
public void propertySourceKeyDataChangeInvalidatesCache() { public void simpleMapPropertySourceKeyDataChangeInvalidatesCache() {
// gh-13344 // gh-13344
Map<String, Object> map = new LinkedHashMap<>(); Map<String, Object> map = new LinkedHashMap<>();
map.put("key1", "value1"); map.put("key1", "value1");
@ -184,10 +185,36 @@ public class SpringIterableConfigurationPropertySourceTests {
source, DefaultPropertyMapper.INSTANCE); source, DefaultPropertyMapper.INSTANCE);
assertThat(adapter.stream().count()).isEqualTo(2); assertThat(adapter.stream().count()).isEqualTo(2);
map.setThrowException(true); map.setThrowException(true);
}
public void originTrackedMapPropertySourceKeyAdditionInvalidatesCache() {
// gh-13344
Map<String, Object> map = new LinkedHashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
EnumerablePropertySource<?> source = new OriginTrackedMapPropertySource("test",
map);
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
source, DefaultPropertyMapper.INSTANCE);
assertThat(adapter.stream().count()).isEqualTo(2);
map.put("key3", "value3"); map.put("key3", "value3");
assertThat(adapter.stream().count()).isEqualTo(3); assertThat(adapter.stream().count()).isEqualTo(3);
} }
public void readOnlyOriginTrackedMapPropertySourceKeyAdditionDoesNotInvalidateCache() {
// gh-16717
Map<String, Object> map = new LinkedHashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
EnumerablePropertySource<?> source = new OriginTrackedMapPropertySource("test",
map, true);
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
source, DefaultPropertyMapper.INSTANCE);
assertThat(adapter.stream().count()).isEqualTo(2);
map.put("key3", "value3");
assertThat(adapter.stream().count()).isEqualTo(2);
}
/** /**
* Test {@link PropertySource} that's also an {@link OriginLookup}. * Test {@link PropertySource} that's also an {@link OriginLookup}.
*/ */

Loading…
Cancel
Save