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 8e484ef3cad..44bd2e09cae 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 @@ -274,11 +274,13 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope if (configurationPropertyName != null && !configurationPropertyName.isEmpty()) { add(mappings, configurationPropertyName, propertyName); reverseMappings.put(propertyName, configurationPropertyName); - addParents(descendants, configurationPropertyName); } } } } + for (String propertyName : propertyNames) { + addParents(descendants, reverseMappings.get(propertyName)); + } ConfigurationPropertyName[] configurationPropertyNames = this.immutable ? reverseMappings.values().toArray(new ConfigurationPropertyName[0]) : null; lastUpdated = this.immutable ? null : propertyNames; @@ -296,7 +298,7 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope } private void addParents(Set descendants, ConfigurationPropertyName name) { - if (descendants == null || name.isEmpty()) { + if (descendants == null || name == null || name.isEmpty()) { return; } ConfigurationPropertyName parent = name.getParent(); 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 0d44809834c..65260348021 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 @@ -239,6 +239,21 @@ class SpringIterableConfigurationPropertySourceTests { "test.map.bravo", "test.map.charlie", "test.map.delta"); } + @Test + void cacheRefreshRecalculatesDescendants() { + // gh-45639 + Map map = new LinkedHashMap<>(); + map.put("one.two.three", "test"); + EnumerablePropertySource source = new OriginTrackedMapPropertySource("test", map, false); + SpringIterableConfigurationPropertySource propertySource = new SpringIterableConfigurationPropertySource(source, + false, DefaultPropertyMapper.INSTANCE); + assertThat(propertySource.containsDescendantOf(ConfigurationPropertyName.of("one.two"))) + .isEqualTo(ConfigurationPropertyState.PRESENT); + map.put("new", "value"); + assertThat(propertySource.containsDescendantOf(ConfigurationPropertyName.of("one.two"))) + .isEqualTo(ConfigurationPropertyState.PRESENT); + } + /** * Test {@link PropertySource} that's also an {@link OriginLookup}. *