Skip to content

Commit dc1c459

Browse files
committed
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
1 parent c556d2b commit dc1c459

File tree

2 files changed

+56
-19
lines changed

2 files changed

+56
-19
lines changed

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

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.Collections;
21+
import java.util.HashSet;
2122
import java.util.Iterator;
2223
import java.util.List;
24+
import java.util.Set;
2325
import java.util.stream.Stream;
2426

2527
import org.springframework.core.env.EnumerablePropertySource;
@@ -129,23 +131,18 @@ private PropertyMapping[] getPropertyMappings(Cache cache) {
129131
}
130132

131133
private Cache getCache() {
132-
Object cacheKey = getCacheKey();
134+
CacheKey cacheKey = CacheKey.get(getPropertySource());
133135
if (cacheKey == null) {
134136
return null;
135137
}
136138
if (ObjectUtils.nullSafeEquals(cacheKey, this.cacheKey)) {
137139
return this.cache;
138140
}
139141
this.cache = new Cache();
140-
this.cacheKey = cacheKey;
142+
this.cacheKey = cacheKey.copy();
141143
return this.cache;
142144
}
143145

144-
private Object getCacheKey() {
145-
// gh-13344
146-
return getPropertySource().getPropertyNames();
147-
}
148-
149146
@Override
150147
protected EnumerablePropertySource<?> getPropertySource() {
151148
return (EnumerablePropertySource<?>) super.getPropertySource();
@@ -175,4 +172,48 @@ public void setMappings(PropertyMapping[] mappings) {
175172

176173
}
177174

175+
private static final class CacheKey {
176+
177+
private Object key;
178+
179+
private CacheKey(Object key) {
180+
this.key = key;
181+
}
182+
183+
public CacheKey copy() {
184+
return new CacheKey(copyKey(this.key));
185+
}
186+
187+
private Object copyKey(Object key) {
188+
if (key instanceof Set) {
189+
return new HashSet<Object>((Set<?>) key);
190+
}
191+
return ((String[]) key).clone();
192+
}
193+
194+
@Override
195+
public int hashCode() {
196+
return this.key.hashCode();
197+
}
198+
199+
@Override
200+
public boolean equals(Object obj) {
201+
if (this == obj) {
202+
return true;
203+
}
204+
if (obj == null || getClass() != obj.getClass()) {
205+
return false;
206+
}
207+
return ObjectUtils.nullSafeEquals(this.key, ((CacheKey) obj).key);
208+
}
209+
210+
public static CacheKey get(EnumerablePropertySource<?> source) {
211+
if (source instanceof MapPropertySource) {
212+
return new CacheKey(((MapPropertySource) source).getSource().keySet());
213+
}
214+
return new CacheKey(source.getPropertyNames());
215+
}
216+
217+
}
218+
178219
}

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

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -158,21 +158,17 @@ public void containsDescendantOfShouldCheckSourceNames() {
158158
.isEqualTo(ConfigurationPropertyState.ABSENT);
159159
}
160160

161-
@SuppressWarnings("unchecked")
162161
@Test
163-
public void propertySourceChangeReflects() {
162+
public void propertySourceKeyDataChangeInvalidatesCache() {
164163
// gh-13344
165-
final Map<String, Object> source = new LinkedHashMap<>();
166-
source.put("key1", "value1");
167-
source.put("key2", "value2");
168-
final EnumerablePropertySource<?> propertySource = new MapPropertySource("test",
169-
source);
170-
final SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
171-
propertySource, DefaultPropertyMapper.INSTANCE);
164+
Map<String, Object> map = new LinkedHashMap<>();
165+
map.put("key1", "value1");
166+
map.put("key2", "value2");
167+
EnumerablePropertySource<?> source = new MapPropertySource("test", map);
168+
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
169+
source, DefaultPropertyMapper.INSTANCE);
172170
assertThat(adapter.stream().count()).isEqualTo(2);
173-
174-
((Map<String, Object>) adapter.getPropertySource().getSource()).put("key3",
175-
"value3");
171+
map.put("key3", "value3");
176172
assertThat(adapter.stream().count()).isEqualTo(3);
177173
}
178174

0 commit comments

Comments
 (0)