diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java index bb6081899c..faea9acf24 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java @@ -103,14 +103,17 @@ class SpringApplicationRunListeners { } private void doWithListeners(String stepName, Consumer listenerAction) { - doWithListeners(stepName, listenerAction, StartupStep::end); + doWithListeners(stepName, listenerAction, null); } private void doWithListeners(String stepName, Consumer listenerAction, Consumer stepAction) { StartupStep step = this.applicationStartup.start(stepName); this.listeners.forEach(listenerAction); - stepAction.accept(step); + if (stepAction != null) { + stepAction.accept(step); + } + step.end(); } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index 844365685c..263f7c3409 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -141,6 +141,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; * @author Madhura Bhave * @author Brian Clozel * @author Artsiom Yudovin + * @author Marten Deinum */ @ExtendWith(OutputCaptureExtension.class) class SpringApplicationTests { @@ -1179,6 +1180,29 @@ class SpringApplicationTests { assertThat(startCount).isEqualTo(endCount); } + @Test + void customApplicationStartupPublishStartupStepsWithFailure() { + ApplicationStartup applicationStartup = mock(ApplicationStartup.class); + StartupStep startupStep = mock(StartupStep.class); + given(applicationStartup.start(anyString())).willReturn(startupStep); + given(startupStep.tag(anyString(), anyString())).willReturn(startupStep); + given(startupStep.tag(anyString(), ArgumentMatchers.>any())).willReturn(startupStep); + SpringApplication application = new SpringApplication(BrokenPostConstructConfig.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.setApplicationStartup(applicationStartup); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(application::run); + verify(applicationStartup).start("spring.boot.application.starting"); + verify(applicationStartup).start("spring.boot.application.environment-prepared"); + verify(applicationStartup).start("spring.boot.application.failed"); + long startCount = mockingDetails(applicationStartup).getInvocations().stream() + .filter((invocation) -> invocation.getMethod().toString().contains("start(")).count(); + long endCount = mockingDetails(startupStep).getInvocations().stream() + .filter((invocation) -> invocation.getMethod().toString().contains("end(")).count(); + assertThat(startCount).isEqualTo(endCount + 1); // Will be same after + // spring-framework #25572 is + // fixed + } + private ArgumentMatcher isAvailabilityChangeEventWithState( S state) { return (argument) -> (argument instanceof AvailabilityChangeEvent)