Browse Source

Fix inconsistent ordering of config imports

Update `ConfigDataEnvironment` so `spring.config.import`
properties defined in environment or system properties
are ordered correctly.

See gh-49324

Signed-off-by: qnnn <qiunan@cmbchina.com>
pull/49591/head
qnnn 3 weeks ago committed by Phillip Webb
parent
commit
1ea07eeb29
  1. 14
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java
  2. 27
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java
  3. 16
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java
  4. 20
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java

14
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
package org.springframework.boot.context.config;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
@ -57,6 +58,7 @@ import org.springframework.util.StringUtils; @@ -57,6 +58,7 @@ import org.springframework.util.StringUtils;
*
* @author Phillip Webb
* @author Madhura Bhave
* @author Nan Chiu
*/
class ConfigDataEnvironment {
@ -197,7 +199,8 @@ class ConfigDataEnvironment { @@ -197,7 +199,8 @@ class ConfigDataEnvironment {
private List<ConfigDataEnvironmentContributor> getInitialImportContributors(Binder binder) {
List<ConfigDataEnvironmentContributor> initialContributors = new ArrayList<>();
addInitialImportContributors(initialContributors, bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS));
addInitialImportPropertyContributors(initialContributors,
bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS));
addInitialImportContributors(initialContributors,
bindLocations(binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS));
addInitialImportContributors(initialContributors,
@ -209,6 +212,15 @@ class ConfigDataEnvironment { @@ -209,6 +212,15 @@ class ConfigDataEnvironment {
return binder.bind(propertyName, CONFIG_DATA_LOCATION_ARRAY).orElse(other);
}
private void addInitialImportPropertyContributors(List<ConfigDataEnvironmentContributor> initialContributors,
ConfigDataLocation[] locations) {
List<ConfigDataEnvironmentContributor> initialPropertiesContributors = new ArrayList<>();
addInitialImportContributors(initialPropertiesContributors, locations);
initialContributors.add(ConfigDataEnvironmentContributor.ofInitialImportProperty(
initialPropertiesContributors, this.environment.getConversionService(), Arrays.asList(locations))
);
}
private void addInitialImportContributors(List<ConfigDataEnvironmentContributor> initialContributors,
ConfigDataLocation[] locations) {
for (int i = locations.length - 1; i >= 0; i--) {

27
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java

@ -52,6 +52,7 @@ import org.springframework.util.CollectionUtils; @@ -52,6 +52,7 @@ import org.springframework.util.CollectionUtils;
*
* @author Phillip Webb
* @author Madhura Bhave
* @author Nan Chiu
*/
class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironmentContributor> {
@ -404,6 +405,27 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment @@ -404,6 +405,27 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
null, null, conversionService);
}
/**
* Factory method to create a {@link Kind#INITIAL_IMPORT_PROPERTY initial import property}
* contributor. This contributor is a container that wraps initial imports from
* {@code spring.config.import} property, allowing profile-specific imports to take
* precedence over the imported locations.
* @param contributors the contributors created from the import locations
* @param conversionService the conversion service to use
* @param locations the original import locations from the property
* @return a new {@link ConfigDataEnvironmentContributor} instance
*/
static ConfigDataEnvironmentContributor ofInitialImportProperty(
List<ConfigDataEnvironmentContributor> contributors,
ConversionService conversionService,
List<ConfigDataLocation> locations) {
Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children = new LinkedHashMap<>();
ConfigDataProperties properties = new ConfigDataProperties(locations, null);
children.put(ImportPhase.BEFORE_PROFILE_ACTIVATION, Collections.unmodifiableList(contributors));
return new ConfigDataEnvironmentContributor(Kind.INITIAL_IMPORT_PROPERTY, null, null, false, null, null, properties, null, children,
conversionService);
}
/**
* Factory method to create a contributor that wraps an {@link Kind#EXISTING existing}
* property source. The contributor provides access to existing properties, but
@ -477,6 +499,11 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment @@ -477,6 +499,11 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
*/
INITIAL_IMPORT,
/**
* A container contributor that wraps initial imports from spring.config.import property.
*/
INITIAL_IMPORT_PROPERTY,
/**
* An existing property source that contributes properties but no imports.
*/

16
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java

@ -44,6 +44,7 @@ import static org.mockito.Mockito.mock; @@ -44,6 +44,7 @@ import static org.mockito.Mockito.mock;
* @author Phillip Webb
* @author Madhura Bhave
* @author Scott Frederick
* @author Nan Chiu
*/
class ConfigDataEnvironmentContributorTests {
@ -298,6 +299,21 @@ class ConfigDataEnvironmentContributorTests { @@ -298,6 +299,21 @@ class ConfigDataEnvironmentContributorTests {
assertThat(contributor.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION)).isEmpty();
}
@Test
void ofInitialImportPropertyCreatedInitialImportPropertyContributor() {
ConfigDataEnvironmentContributor innerContributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION,
this.conversionService);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImportProperty(
Collections.singletonList(innerContributor), this.conversionService, Arrays.asList(TEST_LOCATION));
assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT_PROPERTY);
assertThat(contributor.getResource()).isNull();
assertThat(contributor.getImports()).containsExactly(TEST_LOCATION);
assertThat(contributor.isActive(this.activationContext)).isTrue();
assertThat(contributor.getPropertySource()).isNull();
assertThat(contributor.getConfigurationPropertySource()).isNull();
assertThat(contributor.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION)).containsExactly(innerContributor);
}
@Test
void ofExistingCreatesExistingContributor() {
MockPropertySource propertySource = new MockPropertySource();

20
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java

@ -78,6 +78,7 @@ import static org.assertj.core.api.Assertions.assertThatNoException; @@ -78,6 +78,7 @@ import static org.assertj.core.api.Assertions.assertThatNoException;
*
* @author Madhura Bhave
* @author Phillip Webb
* @author Nan Chiu
*/
class ConfigDataEnvironmentPostProcessorIntegrationTests {
@ -330,6 +331,25 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests { @@ -330,6 +331,25 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
assertThat(property).isEqualTo("frommyprofilepropertiesfile");
}
@Test
@WithResource(name = "testproperties-1.properties", content = """
my.property=fromtestproperties-1.properties
""")
@WithResource(name = "testproperties-1-myprofile.properties", content = """
my.property=fromtestproperties-1-myprofile.properties
""")
@WithResource(name = "testproperties-2.properties", content = """
my.property=fromtestproperties-2.properties
""")
void runWhenHasImportPropertyWithProfileSpecificFileTakesPrecedence() {
ConfigurableApplicationContext context = this.application.run(
"--spring.config.import=classpath:testproperties-1.properties,classpath:testproperties-2.properties",
"--spring.profiles.active=myprofile");
ConfigurableEnvironment environment = context.getEnvironment();
String property = environment.getProperty("my.property");
assertThat(property).isEqualTo("fromtestproperties-1-myprofile.properties");
}
@Test
void runWhenHasLocalFileLoadsWithLocalFileTakingPrecedenceOverClasspath() throws Exception {
File localFile = new File(new File("."), "application.properties");

Loading…
Cancel
Save