Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -59,6 +60,7 @@
*
* @author Phillip Webb
* @author Madhura Bhave
* @author Nan Chiu
*/
class ConfigDataEnvironment {

Expand Down Expand Up @@ -199,7 +201,8 @@ ConfigDataEnvironmentContributors getContributors() {

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,
Expand All @@ -211,6 +214,15 @@ private ConfigDataLocation[] bindLocations(Binder binder, String propertyName, C
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--) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
*
* @author Phillip Webb
* @author Madhura Bhave
* @author Nan Chiu
*/
class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironmentContributor> {

Expand Down Expand Up @@ -413,6 +414,27 @@ static ConfigDataEnvironmentContributor ofInitialImport(ConfigDataLocation initi
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
Expand Down Expand Up @@ -488,6 +510,11 @@ enum Kind {
*/
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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
* @author Phillip Webb
* @author Madhura Bhave
* @author Scott Frederick
* @author Nan Chiu
*/
class ConfigDataEnvironmentContributorTests {

Expand Down Expand Up @@ -303,6 +304,21 @@ void ofInitialImportCreatedInitialImportContributor() {
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
*
* @author Madhura Bhave
* @author Phillip Webb
* @author Nan Chiu
*/
class ConfigDataEnvironmentPostProcessorIntegrationTests {

Expand Down Expand Up @@ -332,6 +333,25 @@ void runWhenHasActiveProfilesFromMultipleAdditionalLocationsWithOneSwitchedOffLo
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");
Expand Down