Commit dc1c459c authored by Phillip Webb's avatar Phillip Webb

Polish "Fix caching issues with map property sources"

Refine the property source cache key fix so that a copy of the
key is only taken when the values change. This allows us to
retain the previous performance optimization of not creating
unnecessary string arrays.

Closes gh-13344
parent c556d2b5
...@@ -18,8 +18,10 @@ package org.springframework.boot.context.properties.source; ...@@ -18,8 +18,10 @@ package org.springframework.boot.context.properties.source;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.EnumerablePropertySource;
...@@ -129,7 +131,7 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope ...@@ -129,7 +131,7 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
} }
private Cache getCache() { private Cache getCache() {
Object cacheKey = getCacheKey(); CacheKey cacheKey = CacheKey.get(getPropertySource());
if (cacheKey == null) { if (cacheKey == null) {
return null; return null;
} }
...@@ -137,15 +139,10 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope ...@@ -137,15 +139,10 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
return this.cache; return this.cache;
} }
this.cache = new Cache(); this.cache = new Cache();
this.cacheKey = cacheKey; this.cacheKey = cacheKey.copy();
return this.cache; return this.cache;
} }
private Object getCacheKey() {
// gh-13344
return getPropertySource().getPropertyNames();
}
@Override @Override
protected EnumerablePropertySource<?> getPropertySource() { protected EnumerablePropertySource<?> getPropertySource() {
return (EnumerablePropertySource<?>) super.getPropertySource(); return (EnumerablePropertySource<?>) super.getPropertySource();
...@@ -175,4 +172,48 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope ...@@ -175,4 +172,48 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope
} }
private static final class CacheKey {
private Object key;
private CacheKey(Object key) {
this.key = key;
}
public CacheKey copy() {
return new CacheKey(copyKey(this.key));
}
private Object copyKey(Object key) {
if (key instanceof Set) {
return new HashSet<Object>((Set<?>) key);
}
return ((String[]) key).clone();
}
@Override
public int hashCode() {
return this.key.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return ObjectUtils.nullSafeEquals(this.key, ((CacheKey) obj).key);
}
public static CacheKey get(EnumerablePropertySource<?> source) {
if (source instanceof MapPropertySource) {
return new CacheKey(((MapPropertySource) source).getSource().keySet());
}
return new CacheKey(source.getPropertyNames());
}
}
} }
...@@ -158,21 +158,17 @@ public class SpringIterableConfigurationPropertySourceTests { ...@@ -158,21 +158,17 @@ public class SpringIterableConfigurationPropertySourceTests {
.isEqualTo(ConfigurationPropertyState.ABSENT); .isEqualTo(ConfigurationPropertyState.ABSENT);
} }
@SuppressWarnings("unchecked")
@Test @Test
public void propertySourceChangeReflects() { public void propertySourceKeyDataChangeInvalidatesCache() {
// gh-13344 // gh-13344
final Map<String, Object> source = new LinkedHashMap<>(); Map<String, Object> map = new LinkedHashMap<>();
source.put("key1", "value1"); map.put("key1", "value1");
source.put("key2", "value2"); map.put("key2", "value2");
final EnumerablePropertySource<?> propertySource = new MapPropertySource("test", EnumerablePropertySource<?> source = new MapPropertySource("test", map);
source); SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
final SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource( source, DefaultPropertyMapper.INSTANCE);
propertySource, DefaultPropertyMapper.INSTANCE);
assertThat(adapter.stream().count()).isEqualTo(2); assertThat(adapter.stream().count()).isEqualTo(2);
map.put("key3", "value3");
((Map<String, Object>) adapter.getPropertySource().getSource()).put("key3",
"value3");
assertThat(adapter.stream().count()).isEqualTo(3); assertThat(adapter.stream().count()).isEqualTo(3);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment