diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java index 648c5361..b67846d8 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java @@ -16,6 +16,7 @@ package org.springframework.cloud.bootstrap; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -46,6 +47,7 @@ import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.SystemEnvironmentPropertySource; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** @@ -81,26 +83,56 @@ public class BootstrapApplicationListener if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { return; } - for (ApplicationContextInitializer initializer : event.getSpringApplication().getInitializers()) { + ConfigurableApplicationContext context = null; + String configName = environment + .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); + for (ApplicationContextInitializer initializer : event.getSpringApplication() + .getInitializers()) { if (initializer instanceof ParentContextApplicationContextInitializer) { - return; + context = findBootstrapContext( + (ParentContextApplicationContextInitializer) initializer, + configName); } } - ConfigurableApplicationContext context = bootstrapServiceContext(environment, - event.getSpringApplication()); + if (context == null) { + context = bootstrapServiceContext(environment, event.getSpringApplication(), + configName); + } apply(context, event.getSpringApplication(), environment); } + private ConfigurableApplicationContext findBootstrapContext( + ParentContextApplicationContextInitializer initializer, String configName) { + Field field = ReflectionUtils + .findField(ParentContextApplicationContextInitializer.class, "parent"); + ReflectionUtils.makeAccessible(field); + ConfigurableApplicationContext parent = safeCast( + ConfigurableApplicationContext.class, + ReflectionUtils.getField(field, initializer)); + if (parent != null && !configName.equals(parent.getId())) { + parent = safeCast(ConfigurableApplicationContext.class, parent.getParent()); + } + return parent; + } + + private T safeCast(Class type, Object object) { + try { + return type.cast(object); + } + catch (ClassCastException e) { + return null; + } + } + private ConfigurableApplicationContext bootstrapServiceContext( - ConfigurableEnvironment environment, final SpringApplication application) { + ConfigurableEnvironment environment, final SpringApplication application, + String configName) { StandardEnvironment bootstrapEnvironment = new StandardEnvironment(); MutablePropertySources bootstrapProperties = bootstrapEnvironment .getPropertySources(); for (PropertySource source : bootstrapProperties) { bootstrapProperties.remove(source.getName()); } - String configName = environment - .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); String configLocation = environment .resolvePlaceholders("${spring.cloud.bootstrap.location:}"); Map bootstrapMap = new HashMap<>(); diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/TestBootstrapConfiguration.java b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/TestBootstrapConfiguration.java index 798db7b8..7265a3a2 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/TestBootstrapConfiguration.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/TestBootstrapConfiguration.java @@ -1,6 +1,14 @@ package org.springframework.cloud.bootstrap; +import java.util.Collections; + +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; import static org.springframework.cloud.bootstrap.TestHigherPriorityBootstrapConfiguration.firstToBeCreated; @@ -8,10 +16,28 @@ import static org.springframework.cloud.bootstrap.TestHigherPriorityBootstrapCon * @author Spencer Gibb */ @Order(0) +@Configuration public class TestBootstrapConfiguration { public TestBootstrapConfiguration() { firstToBeCreated.compareAndSet(null, TestBootstrapConfiguration.class); } + @Bean + public ApplicationContextInitializer customInitializer() { + return new ApplicationContextInitializer() { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + environment.getPropertySources() + .addLast(new MapPropertySource("customProperties", + Collections.singletonMap("custom.foo", + environment.resolvePlaceholders( + "${spring.application.name:bar}")))); + } + + }; + } + } diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java index 87ad1f3a..f6f8ce33 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java @@ -25,6 +25,7 @@ import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; + import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -54,6 +55,8 @@ public class BootstrapConfigurationTests { private ConfigurableApplicationContext context; + private ConfigurableApplicationContext sibling; + @Rule public ExpectedException expected = ExpectedException.none(); @@ -68,6 +71,9 @@ public class BootstrapConfigurationTests { if (this.context != null) { this.context.close(); } + if (this.sibling != null) { + this.sibling.close(); + } } @Test @@ -197,7 +203,7 @@ public class BootstrapConfigurationTests { PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "true"); ConfigurableEnvironment environment = new StandardEnvironment(); environment.getPropertySources().addLast(new MapPropertySource("last", - Collections. singletonMap("bootstrap.foo", "splat"))); + Collections.singletonMap("bootstrap.foo", "splat"))); this.context = new SpringApplicationBuilder().web(false).environment(environment) .sources(BareConfiguration.class).run(); assertEquals("splat", this.context.getEnvironment().getProperty("bootstrap.foo")); @@ -282,6 +288,32 @@ public class BootstrapConfigurationTests { assertNotNull(context.getParent()); assertEquals("bootstrap", context.getParent().getParent().getId()); assertNull(context.getParent().getParent().getParent()); + assertEquals("bar", context.getEnvironment().getProperty("custom.foo")); + } + + @Test + public void bootstrapContextSharedBySiblings() { + TestHigherPriorityBootstrapConfiguration.count.set(0); + PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); + SpringApplicationBuilder builder = new SpringApplicationBuilder() + .sources(BareConfiguration.class); + this.sibling = builder.child(BareConfiguration.class) + .properties("spring.application.name=sibling").web(false).run(); + this.context = builder.child(BareConfiguration.class) + .properties("spring.application.name=context").web(false).run(); + assertEquals(1, TestHigherPriorityBootstrapConfiguration.count.get()); + assertNotNull(context.getParent()); + assertEquals("bootstrap", context.getParent().getParent().getId()); + assertNull(context.getParent().getParent().getParent()); + assertEquals("context", context.getEnvironment().getProperty("custom.foo")); + assertEquals("context", + context.getEnvironment().getProperty("spring.application.name")); + assertNotNull(sibling.getParent()); + assertEquals("bootstrap", sibling.getParent().getParent().getId()); + assertNull(sibling.getParent().getParent().getParent()); + assertEquals("sibling", sibling.getEnvironment().getProperty("custom.foo")); + assertEquals("sibling", + sibling.getEnvironment().getProperty("spring.application.name")); } @Test @@ -340,7 +372,7 @@ public class BootstrapConfigurationTests { protected static class PropertySourceConfiguration implements PropertySourceLocator { public static Map MAP = new HashMap( - Collections. singletonMap("bootstrap.foo", "bar")); + Collections.singletonMap("bootstrap.foo", "bar")); private String name;