Skip to content

Commit 181d0ee

Browse files
committed
Merge default property sources
Fixes gh-25408
1 parent ca41473 commit 181d0ee

File tree

4 files changed

+87
-1
lines changed

4 files changed

+87
-1
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/DefaultPropertiesPropertySource.java

+35
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot;
1818

19+
import java.util.HashMap;
1920
import java.util.Map;
2021
import java.util.function.Consumer;
2122

@@ -72,6 +73,40 @@ public static void ifNotEmpty(Map<String, Object> source, Consumer<DefaultProper
7273
}
7374
}
7475

76+
/**
77+
* Add a new {@link DefaultPropertiesPropertySource} or merge with an existing one.
78+
* @param source the {@code Map} source
79+
* @param sources the existing sources
80+
* @since 2.4.4
81+
*/
82+
public static void addOrMerge(Map<String, Object> source, MutablePropertySources sources) {
83+
if (!CollectionUtils.isEmpty(source)) {
84+
Map<String, Object> resultingSource = new HashMap<>();
85+
DefaultPropertiesPropertySource propertySource = new DefaultPropertiesPropertySource(resultingSource);
86+
if (sources.contains(NAME)) {
87+
mergeIfPossible(source, sources, resultingSource);
88+
sources.replace(NAME, propertySource);
89+
}
90+
else {
91+
resultingSource.putAll(source);
92+
sources.addLast(propertySource);
93+
}
94+
}
95+
}
96+
97+
@SuppressWarnings("unchecked")
98+
private static void mergeIfPossible(Map<String, Object> source, MutablePropertySources sources,
99+
Map<String, Object> resultingSource) {
100+
PropertySource<?> existingSource = sources.get(NAME);
101+
if (existingSource != null) {
102+
Object underlyingSource = existingSource.getSource();
103+
if (underlyingSource instanceof Map) {
104+
resultingSource.putAll((Map<String, Object>) underlyingSource);
105+
}
106+
resultingSource.putAll(source);
107+
}
108+
}
109+
75110
/**
76111
* Move the 'defaultProperties' property source so that it's the last source in the
77112
* given {@link ConfigurableEnvironment}.

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,9 @@ protected void configureEnvironment(ConfigurableEnvironment environment, String[
513513
*/
514514
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
515515
MutablePropertySources sources = environment.getPropertySources();
516-
DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);
516+
if (!CollectionUtils.isEmpty(this.defaultProperties)) {
517+
DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
518+
}
517519
if (this.addCommandLineProperties && args.length > 0) {
518520
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
519521
if (sources.contains(name)) {

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/DefaultPropertiesPropertySourceTests.java

+35
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.mockito.Mock;
2727
import org.mockito.junit.jupiter.MockitoExtension;
2828

29+
import org.springframework.core.env.CompositePropertySource;
2930
import org.springframework.core.env.MutablePropertySources;
3031
import org.springframework.core.env.PropertySource;
3132
import org.springframework.mock.env.MockEnvironment;
@@ -39,6 +40,7 @@
3940
* Tests for {@link DefaultPropertiesPropertySource}.
4041
*
4142
* @author Phillip Webb
43+
* @author Madhura Bhave
4244
*/
4345
@ExtendWith(MockitoExtension.class)
4446
class DefaultPropertiesPropertySourceTests {
@@ -104,6 +106,39 @@ void moveToEndWhenNotPresentDoesNothing() {
104106
DefaultPropertiesPropertySource.moveToEnd(environment);
105107
}
106108

109+
@Test
110+
void addOrMergeWhenExistingNotFoundShouldAdd() {
111+
MockEnvironment environment = new MockEnvironment();
112+
MutablePropertySources propertySources = environment.getPropertySources();
113+
DefaultPropertiesPropertySource.addOrMerge(Collections.singletonMap("spring", "boot"), propertySources);
114+
assertThat(propertySources.contains(DefaultPropertiesPropertySource.NAME)).isTrue();
115+
assertThat(propertySources.get(DefaultPropertiesPropertySource.NAME).getProperty("spring")).isEqualTo("boot");
116+
}
117+
118+
@Test
119+
void addOrMergeWhenExistingFoundShouldMerge() {
120+
MockEnvironment environment = new MockEnvironment();
121+
MutablePropertySources propertySources = environment.getPropertySources();
122+
propertySources.addLast(new DefaultPropertiesPropertySource(Collections.singletonMap("spring", "boot")));
123+
DefaultPropertiesPropertySource.addOrMerge(Collections.singletonMap("hello", "world"), propertySources);
124+
assertThat(propertySources.contains(DefaultPropertiesPropertySource.NAME)).isTrue();
125+
assertThat(propertySources.get(DefaultPropertiesPropertySource.NAME).getProperty("spring")).isEqualTo("boot");
126+
assertThat(propertySources.get(DefaultPropertiesPropertySource.NAME).getProperty("hello")).isEqualTo("world");
127+
}
128+
129+
@Test
130+
void addOrMergeWhenExistingNotMapPropertySourceShouldNotMerge() {
131+
MockEnvironment environment = new MockEnvironment();
132+
MutablePropertySources propertySources = environment.getPropertySources();
133+
CompositePropertySource composite = new CompositePropertySource(DefaultPropertiesPropertySource.NAME);
134+
composite.addPropertySource(new DefaultPropertiesPropertySource(Collections.singletonMap("spring", "boot")));
135+
propertySources.addFirst(composite);
136+
DefaultPropertiesPropertySource.addOrMerge(Collections.singletonMap("hello", "world"), propertySources);
137+
assertThat(propertySources.contains(DefaultPropertiesPropertySource.NAME)).isTrue();
138+
assertThat(propertySources.get(DefaultPropertiesPropertySource.NAME).getProperty("spring")).isNull();
139+
assertThat(propertySources.get(DefaultPropertiesPropertySource.NAME).getProperty("hello")).isEqualTo("world");
140+
}
141+
107142
@Test
108143
void moveToEndWhenPresentMovesToEnd() {
109144
MockEnvironment environment = new MockEnvironment();

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java

+14
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import org.springframework.boot.availability.AvailabilityState;
5454
import org.springframework.boot.availability.LivenessState;
5555
import org.springframework.boot.availability.ReadinessState;
56+
import org.springframework.boot.builder.SpringApplicationBuilder;
5657
import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
5758
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
5859
import org.springframework.boot.context.event.ApplicationFailedEvent;
@@ -105,6 +106,7 @@
105106
import org.springframework.core.metrics.ApplicationStartup;
106107
import org.springframework.core.metrics.StartupStep;
107108
import org.springframework.http.server.reactive.HttpHandler;
109+
import org.springframework.mock.env.MockEnvironment;
108110
import org.springframework.test.context.support.TestPropertySourceUtils;
109111
import org.springframework.util.LinkedMultiValueMap;
110112
import org.springframework.util.MultiValueMap;
@@ -887,6 +889,18 @@ void defaultCommandLineArgs() {
887889
assertThat(getEnvironment().getProperty("baz")).isEqualTo("");
888890
}
889891

892+
@Test
893+
void defaultPropertiesShouldBeMerged() {
894+
MockEnvironment environment = new MockEnvironment();
895+
environment.getPropertySources().addFirst(
896+
new MapPropertySource(DefaultPropertiesPropertySource.NAME, Collections.singletonMap("bar", "foo")));
897+
SpringApplication application = new SpringApplicationBuilder(ExampleConfig.class).environment(environment)
898+
.properties("baz=bing").web(WebApplicationType.NONE).build();
899+
this.context = application.run();
900+
assertThat(getEnvironment().getProperty("bar")).isEqualTo("foo");
901+
assertThat(getEnvironment().getProperty("baz")).isEqualTo("bing");
902+
}
903+
890904
@Test
891905
void commandLineArgsApplyToSpringApplication() {
892906
TestSpringApplication application = new TestSpringApplication(ExampleConfig.class);

0 commit comments

Comments
 (0)