diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java index fda347cadc..909c38f890 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Stream; +import org.springframework.boot.env.OriginTrackedMapPropertySource; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; @@ -192,21 +193,37 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope private final Object key; - private CacheKey(Object key) { + private final int size; + + private final boolean unmodifiableKey; + + private CacheKey(Object key, boolean unmodifiableKey) { this.key = key; + this.size = calculateSize(key); + this.unmodifiableKey = unmodifiableKey; } public CacheKey copy() { - return new CacheKey(copyKey(this.key)); + return new CacheKey(copyKey(this.key), this.unmodifiableKey); } private Object copyKey(Object key) { + if (this.unmodifiableKey) { + return key; + } if (key instanceof Set) { return new HashSet((Set) key); } return ((String[]) key).clone(); } + private int calculateSize(Object key) { + if (key instanceof Set) { + return ((Set) key).size(); + } + return ((String[]) key).length; + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -215,7 +232,11 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope if (obj == null || getClass() != obj.getClass()) { return false; } - return ObjectUtils.nullSafeEquals(this.key, ((CacheKey) obj).key); + CacheKey otherCacheKey = (CacheKey) obj; + if (this.size != otherCacheKey.size) { + return false; + } + return ObjectUtils.nullSafeEquals(this.key, otherCacheKey.key); } @Override @@ -225,9 +246,10 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope public static CacheKey get(EnumerablePropertySource source) { if (source instanceof MapPropertySource) { - return new CacheKey(((MapPropertySource) source).getSource().keySet()); + return new CacheKey(((MapPropertySource) source).getSource().keySet(), + source instanceof OriginTrackedMapPropertySource); } - return new CacheKey(source.getPropertyNames()); + return new CacheKey(source.getPropertyNames(), false); } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySourceTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySourceTests.java index 96a96be3d2..eb1bb8135e 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySourceTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySourceTests.java @@ -25,6 +25,7 @@ import java.util.Set; import org.junit.Test; +import org.springframework.boot.env.OriginTrackedMapPropertySource; import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.OriginLookup; import org.springframework.core.env.EnumerablePropertySource; @@ -160,7 +161,7 @@ public class SpringIterableConfigurationPropertySourceTests { } @Test - public void propertySourceKeyDataChangeInvalidatesCache() { + public void simpleMapPropertySourceKeyDataChangeInvalidatesCache() { // gh-13344 Map map = new LinkedHashMap<>(); map.put("key1", "value1"); @@ -184,6 +185,18 @@ public class SpringIterableConfigurationPropertySourceTests { source, DefaultPropertyMapper.INSTANCE); assertThat(adapter.stream().count()).isEqualTo(2); map.setThrowException(true); + } + + public void originTrackedMapPropertySourceKeyAdditionInvalidatesCache() { + // gh-13344 + Map 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"); assertThat(adapter.stream().count()).isEqualTo(3); }