diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationShutdownHook.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationShutdownHook.java index cc4faff521..128d0c6cfb 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationShutdownHook.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationShutdownHook.java @@ -24,6 +24,7 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -59,26 +60,16 @@ class SpringApplicationShutdownHook implements Runnable { private final ApplicationContextClosedListener contextCloseListener = new ApplicationContextClosedListener(); - private boolean inProgress; + private final AtomicBoolean shutdownHookAdded = new AtomicBoolean(false); - SpringApplicationShutdownHook() { - try { - addRuntimeShutdownHook(); - } - catch (AccessControlException ex) { - // Not allowed in some environments - } - } - - protected void addRuntimeShutdownHook() { - Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook")); - } + private boolean inProgress; SpringApplicationShutdownHandlers getHandlers() { return this.handlers; } void registerApplicationContext(ConfigurableApplicationContext context) { + addRuntimeShutdownHookIfNecessary(); synchronized (SpringApplicationShutdownHook.class) { assertNotInProgress(); context.addApplicationListener(this.contextCloseListener); @@ -86,6 +77,21 @@ class SpringApplicationShutdownHook implements Runnable { } } + private void addRuntimeShutdownHookIfNecessary() { + if (this.shutdownHookAdded.compareAndSet(false, true)) { + addRuntimeShutdownHook(); + } + } + + void addRuntimeShutdownHook() { + try { + Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook")); + } + catch (AccessControlException ex) { + // Not allowed in some environments + } + } + @Override public void run() { Set contexts; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationShutdownHookTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationShutdownHookTests.java index d188a1b731..aed3d89ed2 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationShutdownHookTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationShutdownHookTests.java @@ -46,8 +46,11 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; class SpringApplicationShutdownHookTests { @Test - void createCallsRegister() { + void shutdownHookIsNotAddedUntilContextIsRegistered() { TestSpringApplicationShutdownHook shutdownHook = new TestSpringApplicationShutdownHook(); + assertThat(shutdownHook.isRuntimeShutdownHookAdded()).isFalse(); + ConfigurableApplicationContext context = new GenericApplicationContext(); + shutdownHook.registerApplicationContext(context); assertThat(shutdownHook.isRuntimeShutdownHookAdded()).isTrue(); }