diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/refresh/ContextRefresher.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/refresh/ContextRefresher.java index 2b7b59a3..c57a25b8 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/refresh/ContextRefresher.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/refresh/ContextRefresher.java @@ -92,12 +92,9 @@ public class ContextRefresher { target.addAfter(targetName, source); } else { - if (target.contains("defaultProperties")) { - target.addBefore("defaultProperties", source); - } - else { - target.addLast(source); - } + // targetName was null so we are at the start of the list + target.addFirst(source); + targetName = name; } } } diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/context/refresh/ContextRefresherTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/context/refresh/ContextRefresherTests.java new file mode 100644 index 00000000..1c1a0251 --- /dev/null +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/context/refresh/ContextRefresherTests.java @@ -0,0 +1,96 @@ +package org.springframework.cloud.context.refresh; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.After; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.cloud.bootstrap.config.PropertySourceLocator; +import org.springframework.cloud.context.scope.refresh.RefreshScope; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; + +public class ContextRefresherTests { + + private RefreshScope scope = Mockito.mock(RefreshScope.class); + private ConfigurableApplicationContext context; + + @After + public void close() { + if (context != null) { + context.close(); + } + } + + @Test + public void orderNewPropertiesConsistentWithNewContext() { + context = SpringApplication.run(ContextRefresherTests.class, + "--spring.main.webEnvironment=false", "--debug=false", + "--spring.main.bannerMode=OFF"); + context.getEnvironment().setActiveProfiles("refresh"); + List names = names(context.getEnvironment().getPropertySources()); + assertThat(names).doesNotContain( + "applicationConfig: [classpath:/bootstrap-refresh.properties]"); + ContextRefresher refresher = new ContextRefresher(context, scope); + refresher.refresh(); + names = names(context.getEnvironment().getPropertySources()); + assertThat(names) + .contains("applicationConfig: [classpath:/bootstrap-refresh.properties]"); + assertThat(names).containsSequence( + "applicationConfig: [classpath:/bootstrap-refresh.properties]", + "applicationConfig: [classpath:/bootstrap.properties]"); + } + + @Test + public void bootstrapPropertySourceAlwaysFirst() { + // Use spring.cloud.bootstrap.name to switch off the defaults (which would pick up + // a bootstrapProperties immediately + context = SpringApplication.run(ContextRefresherTests.class, + "--spring.main.webEnvironment=false", "--debug=false", + "--spring.main.bannerMode=OFF", "--spring.cloud.bootstrap.name=refresh"); + List names = names(context.getEnvironment().getPropertySources()); + assertThat(names).doesNotContain("bootstrapProperties"); + ContextRefresher refresher = new ContextRefresher(context, scope); + EnvironmentTestUtils.addEnvironment(context, + "spring.cloud.bootstrap.sources: org.springframework.cloud.context.refresh.ContextRefresherTests.PropertySourceConfiguration\n" + + ""); + refresher.refresh(); + names = names(context.getEnvironment().getPropertySources()); + assertThat(names).first().isEqualTo("bootstrapProperties"); + } + + private List names(MutablePropertySources propertySources) { + List list = new ArrayList<>(); + for (PropertySource p : propertySources) { + list.add(p.getName()); + } + return list; + } + + @Configuration + // This is added to bootstrap context as a source in bootstrap.properties + protected static class PropertySourceConfiguration implements PropertySourceLocator { + + public static Map MAP = new HashMap( + Collections.singletonMap("bootstrap.foo", "refresh")); + + @Override + public PropertySource locate(Environment environment) { + return new MapPropertySource("refreshTest", MAP); + } + + } + +} diff --git a/spring-cloud-context/src/test/resources/bootstrap-refresh.properties b/spring-cloud-context/src/test/resources/bootstrap-refresh.properties new file mode 100644 index 00000000..1339cc1d --- /dev/null +++ b/spring-cloud-context/src/test/resources/bootstrap-refresh.properties @@ -0,0 +1 @@ +info.name: refresh-child