Skip to content

Commit cd36444

Browse files
committed
Extract DefaultPropertiesPropertySource class
Extract `DefaultPropertiesPropertySource` from `SpringApplication` so that logic can be easily accessed by other classes. Specifically the property source name and logic to move the source to the end of the list needs to be called from several places. Closes gh-22520
1 parent 039fbdf commit cd36444

File tree

3 files changed

+222
-4
lines changed

3 files changed

+222
-4
lines changed

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.springframework.boot.context.properties.bind.Binder;
4848
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
4949
import org.springframework.boot.convert.ApplicationConversionService;
50+
import org.springframework.boot.env.DefaultPropertiesPropertySource;
5051
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
5152
import org.springframework.boot.web.reactive.context.StandardReactiveWebEnvironment;
5253
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
@@ -68,7 +69,6 @@
6869
import org.springframework.core.env.CompositePropertySource;
6970
import org.springframework.core.env.ConfigurableEnvironment;
7071
import org.springframework.core.env.Environment;
71-
import org.springframework.core.env.MapPropertySource;
7272
import org.springframework.core.env.MutablePropertySources;
7373
import org.springframework.core.env.PropertySource;
7474
import org.springframework.core.env.SimpleCommandLinePropertySource;
@@ -351,6 +351,7 @@ private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners
351351
configureEnvironment(environment, applicationArguments.getSourceArgs());
352352
ConfigurationPropertySources.attach(environment);
353353
listeners.environmentPrepared(environment);
354+
DefaultPropertiesPropertySource.moveToEnd(environment);
354355
bindToSpringApplication(environment);
355356
if (!this.isCustomEnvironment) {
356357
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
@@ -499,9 +500,7 @@ protected void configureEnvironment(ConfigurableEnvironment environment, String[
499500
*/
500501
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
501502
MutablePropertySources sources = environment.getPropertySources();
502-
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
503-
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
504-
}
503+
DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);
505504
if (this.addCommandLineProperties && args.length > 0) {
506505
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
507506
if (sources.contains(name)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.env;
18+
19+
import java.util.Map;
20+
import java.util.function.Consumer;
21+
22+
import org.springframework.core.env.ConfigurableEnvironment;
23+
import org.springframework.core.env.Environment;
24+
import org.springframework.core.env.MapPropertySource;
25+
import org.springframework.core.env.MutablePropertySources;
26+
import org.springframework.core.env.PropertySource;
27+
import org.springframework.util.CollectionUtils;
28+
29+
/**
30+
* {@link MapPropertySource} containing default properties contributed directly to a
31+
* {@code SpringApplication}. By convention, the {@link DefaultPropertiesPropertySource}
32+
* is always the last property source in the {@link Environment}.
33+
*
34+
* @author Phillip Webb
35+
* @since 2.4.0
36+
*/
37+
public class DefaultPropertiesPropertySource extends MapPropertySource {
38+
39+
/**
40+
* The name of the 'default properties' property source.
41+
*/
42+
public static final String NAME = "defaultProperties";
43+
44+
/**
45+
* Create a new {@link DefaultPropertiesPropertySource} with the given {@code Map}
46+
* source.
47+
* @param source the source map
48+
*/
49+
public DefaultPropertiesPropertySource(Map<String, Object> source) {
50+
super(NAME, source);
51+
}
52+
53+
/**
54+
* Return {@code true} if the given source is named 'defaultProperties'.
55+
* @param propertySource the property source to check
56+
* @return {@code true} if the name matches
57+
*/
58+
public static boolean hasMatchingName(PropertySource<?> propertySource) {
59+
return (propertySource != null) && propertySource.getName().equals(NAME);
60+
}
61+
62+
/**
63+
* Create a consume a new {@link DefaultPropertiesPropertySource} instance if the
64+
* provided source is not empty.
65+
* @param source the {@code Map} source
66+
* @param action the action used to consume the
67+
* {@link DefaultPropertiesPropertySource}
68+
*/
69+
public static void ifNotEmpty(Map<String, Object> source, Consumer<DefaultPropertiesPropertySource> action) {
70+
if (!CollectionUtils.isEmpty(source) && action != null) {
71+
action.accept(new DefaultPropertiesPropertySource(source));
72+
}
73+
}
74+
75+
/**
76+
* Move the 'defaultProperties' property source so that it's the last source in the
77+
* given {@link ConfigurableEnvironment}.
78+
* @param environment the environment to update
79+
*/
80+
public static void moveToEnd(ConfigurableEnvironment environment) {
81+
moveToEnd(environment.getPropertySources());
82+
}
83+
84+
/**
85+
* Move the 'defaultProperties' property source so that it's the last source in the
86+
* given {@link MutablePropertySources}.
87+
* @param propertySources the property sources to update
88+
*/
89+
public static void moveToEnd(MutablePropertySources propertySources) {
90+
PropertySource<?> propertySource = propertySources.remove(NAME);
91+
if (propertySource != null) {
92+
propertySources.addLast(propertySource);
93+
}
94+
}
95+
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.env;
18+
19+
import java.util.Collections;
20+
import java.util.function.Consumer;
21+
22+
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Test;
24+
import org.mockito.ArgumentCaptor;
25+
import org.mockito.Captor;
26+
import org.mockito.Mock;
27+
import org.mockito.MockitoAnnotations;
28+
29+
import org.springframework.core.env.MutablePropertySources;
30+
import org.springframework.core.env.PropertySource;
31+
import org.springframework.mock.env.MockEnvironment;
32+
import org.springframework.mock.env.MockPropertySource;
33+
34+
import static org.assertj.core.api.Assertions.assertThat;
35+
import static org.mockito.Mockito.verify;
36+
import static org.mockito.Mockito.verifyNoInteractions;
37+
38+
/**
39+
* Tests for {@link DefaultPropertiesPropertySource}.
40+
*
41+
* @author Phillip Webb
42+
*/
43+
class DefaultPropertiesPropertySourceTests {
44+
45+
@Mock
46+
private Consumer<DefaultPropertiesPropertySource> action;
47+
48+
@Captor
49+
private ArgumentCaptor<DefaultPropertiesPropertySource> captor;
50+
51+
@BeforeEach
52+
void setup() {
53+
MockitoAnnotations.initMocks(this);
54+
}
55+
56+
@Test
57+
void nameIsDefaultProperties() {
58+
assertThat(DefaultPropertiesPropertySource.NAME).isEqualTo("defaultProperties");
59+
}
60+
61+
@Test
62+
void createCreatesSource() {
63+
DefaultPropertiesPropertySource propertySource = new DefaultPropertiesPropertySource(
64+
Collections.singletonMap("spring", "boot"));
65+
assertThat(propertySource.getName()).isEqualTo("defaultProperties");
66+
assertThat(propertySource.getProperty("spring")).isEqualTo("boot");
67+
}
68+
69+
@Test
70+
void hasMatchingNameWhenNameMatchesReturnsTrue() {
71+
MockPropertySource propertySource = new MockPropertySource("defaultProperties");
72+
assertThat(DefaultPropertiesPropertySource.hasMatchingName(propertySource)).isTrue();
73+
}
74+
75+
@Test
76+
void hasMatchingNameWhenNameDoesNotMatchReturnsFalse() {
77+
MockPropertySource propertySource = new MockPropertySource("normalProperties");
78+
assertThat(DefaultPropertiesPropertySource.hasMatchingName(propertySource)).isFalse();
79+
}
80+
81+
@Test
82+
void hasMatchingNameWhenPropertySourceIsNullReturnsFalse() {
83+
assertThat(DefaultPropertiesPropertySource.hasMatchingName(null)).isFalse();
84+
}
85+
86+
@Test
87+
void ifNotEmptyWhenNullDoesNotCallAction() {
88+
DefaultPropertiesPropertySource.ifNotEmpty(null, this.action);
89+
verifyNoInteractions(this.action);
90+
}
91+
92+
@Test
93+
void ifNotEmptyWhenEmptyDoesNotCallAction() {
94+
DefaultPropertiesPropertySource.ifNotEmpty(Collections.emptyMap(), this.action);
95+
verifyNoInteractions(this.action);
96+
}
97+
98+
@Test
99+
void ifNotEmptyHasValueCallsAction() {
100+
DefaultPropertiesPropertySource.ifNotEmpty(Collections.singletonMap("spring", "boot"), this.action);
101+
verify(this.action).accept(this.captor.capture());
102+
assertThat(this.captor.getValue().getProperty("spring")).isEqualTo("boot");
103+
}
104+
105+
@Test
106+
void moveToEndWhenNotPresentDoesNothing() {
107+
MockEnvironment environment = new MockEnvironment();
108+
DefaultPropertiesPropertySource.moveToEnd(environment);
109+
}
110+
111+
@Test
112+
void moveToEndWhenPresentMovesToEnd() {
113+
MockEnvironment environment = new MockEnvironment();
114+
MutablePropertySources propertySources = environment.getPropertySources();
115+
propertySources.addLast(new DefaultPropertiesPropertySource(Collections.singletonMap("spring", "boot")));
116+
propertySources.addLast(new MockPropertySource("test"));
117+
DefaultPropertiesPropertySource.moveToEnd(environment);
118+
String[] names = propertySources.stream().map(PropertySource::getName).toArray(String[]::new);
119+
assertThat(names).containsExactly(MockPropertySource.MOCK_PROPERTIES_PROPERTY_SOURCE_NAME, "test",
120+
DefaultPropertiesPropertySource.NAME);
121+
}
122+
123+
}

0 commit comments

Comments
 (0)