Apply bootstrap initializers to all contexts in a hierarchy

The fix for gh-153 was sensible, but has now exposed another issue
which is that the bootstrap initializers, whilst they are now only
created once, actually only get applied to the parent context
in a hierarchy.

Most of the time this doesn't matter because the child contexts all
benefit from environment changes and additional beans added
to the parent. If the children are supposed to actually have
different environments, though, there is a problem. So this change
locates the existing bootstrap context and applies it to each
new context created separately.

Unfortunately we have to use reflection to do that for now because
there is no way to discover the parent with a public API before
the context is refreshed.

See gh-190
This commit is contained in:
Dave Syer
2017-03-15 11:27:52 +00:00
parent 70d601df84
commit ba281228f6
3 changed files with 99 additions and 9 deletions

View File

@@ -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<ConfigurableApplicationContext> customInitializer() {
return new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
environment.getPropertySources()
.addLast(new MapPropertySource("customProperties",
Collections.<String, Object>singletonMap("custom.foo",
environment.resolvePlaceholders(
"${spring.application.name:bar}"))));
}
};
}
}

View File

@@ -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.<String, Object> singletonMap("bootstrap.foo", "splat")));
Collections.<String, Object>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<String, Object> MAP = new HashMap<String, Object>(
Collections.<String, Object> singletonMap("bootstrap.foo", "bar"));
Collections.<String, Object>singletonMap("bootstrap.foo", "bar"));
private String name;