diff --git a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index 7aa8fedd571..36d00cef9cb 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -177,6 +177,8 @@ public class SpringApplication { private boolean headless = true; + private boolean registerShutdownHook = true; + private List> initializers; private List> listeners; @@ -284,7 +286,9 @@ public class SpringApplication { // Create, load, refresh and run the ApplicationContext context = createApplicationContext(); - context.registerShutdownHook(); + if (this.registerShutdownHook) { + context.registerShutdownHook(); + } context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); @@ -669,6 +673,15 @@ public class SpringApplication { this.headless = headless; } + /** + * Sets if the created {@link ApplicationContext} should have a shutdown hook + * registered. Defaults to {@code true} to ensure that JVM shutdowns are handled + * gracefully. + */ + public void setRegisterShutdownHook(boolean registerShutdownHook) { + this.registerShutdownHook = registerShutdownHook; + } + /** * Sets if the Spring banner should be displayed when the application runs. Defaults * to {@code true}. diff --git a/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java b/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java index d841dc410d3..33f270c7635 100644 --- a/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java +++ b/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java @@ -79,6 +79,8 @@ public class SpringApplicationBuilder { private Set additionalProfiles = new LinkedHashSet(); + private boolean registerShutdownHookApplied; + public SpringApplicationBuilder(Object... sources) { this.application = new SpringApplication(sources); } @@ -109,8 +111,11 @@ public class SpringApplicationBuilder { public ConfigurableApplicationContext run(String... args) { if (this.parent != null) { - // If there is a parent initialize it and make sure it is added to the current - // context + // If there is a parent don't register a shutdown hook + if (!this.registerShutdownHookApplied) { + this.application.setRegisterShutdownHook(false); + } + // initialize it and make sure it is added to the current context initializers(new ParentContextApplicationContextInitializer( this.parent.run(args))); } @@ -201,6 +206,7 @@ public class SpringApplicationBuilder { this.parent.context = parent; this.parent.running.set(true); initializers(new ParentContextApplicationContextInitializer(parent)); + return this; } @@ -300,6 +306,16 @@ public class SpringApplicationBuilder { return this; } + /** + * Sets if the created {@link ApplicationContext} should have a shutdown hook + * registered. + */ + public SpringApplicationBuilder registerShutdownHook(boolean registerShutdownHook) { + this.registerShutdownHookApplied = true; + this.application.setRegisterShutdownHook(registerShutdownHook); + return this; + } + /** * Fixes the main application class that is used to anchor the startup messages. * @param mainApplicationClass the class to use. diff --git a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index 1c82191a8d0..649bc443336 100644 --- a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -393,6 +393,17 @@ public class SpringApplicationTests { verify(applicationContext.getApplicationContext()).registerShutdownHook(); } + @Test + public void registerShutdownHookOff() throws Exception { + SpringApplication application = new SpringApplication(ExampleConfig.class); + application.setApplicationContextClass(SpyApplicationContext.class); + application.setRegisterShutdownHook(false); + this.context = application.run(); + SpyApplicationContext applicationContext = (SpyApplicationContext) this.context; + verify(applicationContext.getApplicationContext(), never()) + .registerShutdownHook(); + } + @Test public void headless() throws Exception { TestSpringApplication application = new TestSpringApplication(ExampleConfig.class); diff --git a/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java b/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java index 07215914033..5404bf17ffa 100644 --- a/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java @@ -108,6 +108,21 @@ public class SpringApplicationBuilderTests { this.context = application.run(); verify(((SpyApplicationContext) this.context).getApplicationContext()).setParent( any(ApplicationContext.class)); + assertThat(((SpyApplicationContext) this.context).getRegisteredShutdownHook(), + equalTo(false)); + } + + @Test + public void parentContextCreationWithChildShutdown() throws Exception { + SpringApplicationBuilder application = new SpringApplicationBuilder( + ChildConfig.class).contextClass(SpyApplicationContext.class) + .registerShutdownHook(true); + application.parent(ExampleConfig.class); + this.context = application.run(); + verify(((SpyApplicationContext) this.context).getApplicationContext()).setParent( + any(ApplicationContext.class)); + assertThat(((SpyApplicationContext) this.context).getRegisteredShutdownHook(), + equalTo(true)); } @Test @@ -143,6 +158,8 @@ public class SpringApplicationBuilderTests { this.context = application.run(); verify(((SpyApplicationContext) this.context).getApplicationContext()).setParent( any(ApplicationContext.class)); + assertThat(((SpyApplicationContext) this.context).getRegisteredShutdownHook(), + equalTo(false)); } @Test @@ -216,8 +233,11 @@ public class SpringApplicationBuilderTests { public static class SpyApplicationContext extends AnnotationConfigApplicationContext { private final ConfigurableApplicationContext applicationContext = spy(new AnnotationConfigApplicationContext()); + private ResourceLoader resourceLoader; + private boolean registeredShutdownHook; + @Override public void setParent(ApplicationContext parent) { this.applicationContext.setParent(parent); @@ -237,5 +257,14 @@ public class SpringApplicationBuilderTests { return this.resourceLoader; } + @Override + public void registerShutdownHook() { + super.registerShutdownHook(); + this.registeredShutdownHook = true; + } + + public boolean getRegisteredShutdownHook() { + return this.registeredShutdownHook; + } } }