diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartApplicationListener.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartApplicationListener.java index 6969c99e61ae..ee293bd7f0d6 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartApplicationListener.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartApplicationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationStartingEvent; +import org.springframework.boot.devtools.system.DevToolsEnablementDeducer; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; @@ -66,7 +67,14 @@ private void onApplicationStartingEvent(ApplicationStartingEvent event) { String enabled = System.getProperty(ENABLED_PROPERTY); RestartInitializer restartInitializer = null; if (enabled == null) { - restartInitializer = new DefaultRestartInitializer(); + if (implicitlyEnableRestart()) { + restartInitializer = new DefaultRestartInitializer(); + } + else { + logger.info("Restart disabled due to context in which it is running"); + Restarter.disable(); + return; + } } else if (Boolean.parseBoolean(enabled)) { restartInitializer = new DefaultRestartInitializer() { @@ -96,6 +104,10 @@ protected boolean isDevelopmentClassLoader(ClassLoader classLoader) { } } + boolean implicitlyEnableRestart() { + return DevToolsEnablementDeducer.shouldEnable(Thread.currentThread()); + } + private void onApplicationPreparedEvent(ApplicationPreparedEvent event) { Restarter.getInstance().prepare(event.getApplicationContext()); } diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/Restarter.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/Restarter.java index a7830a50c5fd..abf20d32109d 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/Restarter.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/Restarter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -423,7 +423,7 @@ boolean isFinished() { } void prepare(ConfigurableApplicationContext applicationContext) { - if (applicationContext != null && applicationContext.getParent() != null) { + if (!this.enabled || (applicationContext != null && applicationContext.getParent() != null)) { return; } if (applicationContext instanceof GenericApplicationContext) { diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java index 308642994ca7..9e0d0f820925 100644 --- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java +++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,7 +66,7 @@ void isHighestPriority() { @Test void initializeWithReady() { - testInitialize(false); + testInitialize(false, new ImplicitlyEnabledRestartApplicationListener()); assertThat(Restarter.getInstance()).hasFieldOrPropertyWithValue("args", ARGS); assertThat(Restarter.getInstance().isFinished()).isTrue(); assertThat((List) ReflectionTestUtils.getField(Restarter.getInstance(), "rootContexts")).isNotEmpty(); @@ -74,7 +74,7 @@ void initializeWithReady() { @Test void initializeWithFail() { - testInitialize(true); + testInitialize(true, new ImplicitlyEnabledRestartApplicationListener()); assertThat(Restarter.getInstance()).hasFieldOrPropertyWithValue("args", ARGS); assertThat(Restarter.getInstance().isFinished()).isTrue(); assertThat((List) ReflectionTestUtils.getField(Restarter.getInstance(), "rootContexts")).isEmpty(); @@ -83,7 +83,7 @@ void initializeWithFail() { @Test void disableWithSystemProperty(CapturedOutput output) { System.setProperty(ENABLED_PROPERTY, "false"); - testInitialize(false); + testInitialize(false, new ImplicitlyEnabledRestartApplicationListener()); assertThat(Restarter.getInstance()).hasFieldOrPropertyWithValue("enabled", false); assertThat(output).contains("Restart disabled due to System property"); } @@ -91,14 +91,28 @@ void disableWithSystemProperty(CapturedOutput output) { @Test void enableWithSystemProperty(CapturedOutput output) { System.setProperty(ENABLED_PROPERTY, "true"); - testInitialize(false); + testInitialize(false, new ImplicitlyEnabledRestartApplicationListener()); assertThat(Restarter.getInstance()).hasFieldOrPropertyWithValue("enabled", true); assertThat(output).contains("Restart enabled irrespective of application packaging due to System property"); } - private void testInitialize(boolean failed) { + @Test + void enableWithSystemPropertyWhenImplicitlyDisabled(CapturedOutput output) { + System.setProperty(ENABLED_PROPERTY, "true"); + testInitialize(false, new RestartApplicationListener()); + assertThat(Restarter.getInstance()).hasFieldOrPropertyWithValue("enabled", true); + assertThat(output).contains("Restart enabled irrespective of application packaging due to System property"); + } + + @Test + void implicitlyDisabledInTests(CapturedOutput output) { + testInitialize(false, new RestartApplicationListener()); + assertThat(Restarter.getInstance()).hasFieldOrPropertyWithValue("enabled", false); + assertThat(output).contains("Restart disabled due to context in which it is running"); + } + + private void testInitialize(boolean failed, RestartApplicationListener listener) { Restarter.clearInstance(); - RestartApplicationListener listener = new RestartApplicationListener(); DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); SpringApplication application = new SpringApplication(); ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class); @@ -114,4 +128,13 @@ private void testInitialize(boolean failed) { } } + private static class ImplicitlyEnabledRestartApplicationListener extends RestartApplicationListener { + + @Override + boolean implicitlyEnableRestart() { + return true; + } + + } + } diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestarterTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestarterTests.java index 7c329dd78691..75689433fff6 100644 --- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestarterTests.java +++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestarterTests.java @@ -24,6 +24,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; +import org.assertj.core.api.InstanceOfAssertFactories; import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -37,6 +38,7 @@ import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.event.ContextClosedEvent; import org.springframework.scheduling.annotation.EnableScheduling; @@ -44,6 +46,7 @@ import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; +import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; @@ -90,6 +93,14 @@ void testRestart(CapturedOutput output) { }); } + @Test + void testDisabled() { + Restarter.disable(); + ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class); + Restarter.getInstance().prepare(context); + assertThat(Restarter.getInstance()).extracting("rootContexts", as(InstanceOfAssertFactories.LIST)).isEmpty(); + } + @Test @SuppressWarnings("rawtypes") void getOrAddAttributeWithNewAttribute() {