From c89576d14d179a9280b21a7028f21562a1e22eef Mon Sep 17 00:00:00 2001 From: Michael Berry Date: Fri, 28 Nov 2025 00:52:12 +0000 Subject: [PATCH 1/2] Stop closing parent's MeterRegistry beans when child context closes See gh-48319 Signed-off-by: Michael Berry --- .../metrics/MetricsAutoConfiguration.java | 23 ++++++++----------- .../MetricsAutoConfigurationTests.java | 16 +++++++++++++ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java index 2d262a75bfa..9da104ab807 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.metrics; -import java.util.List; - import io.micrometer.core.annotation.Timed; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; @@ -42,6 +40,7 @@ import org.springframework.core.annotation.Order; * @author Jon Schneider * @author Stephane Nicoll * @author Moritz Halbritter + * @author Michael Berry * @since 2.0.0 */ @AutoConfiguration(before = CompositeMeterRegistryAutoConfiguration.class) @@ -71,8 +70,8 @@ public class MetricsAutoConfiguration { } @Bean - MeterRegistryCloser meterRegistryCloser(ObjectProvider meterRegistries) { - return new MeterRegistryCloser(meterRegistries.orderedStream().toList()); + MeterRegistryCloser meterRegistryCloser() { + return new MeterRegistryCloser(); } /** @@ -81,17 +80,15 @@ public class MetricsAutoConfiguration { */ static class MeterRegistryCloser implements ApplicationListener { - private final List meterRegistries; - - MeterRegistryCloser(List meterRegistries) { - this.meterRegistries = meterRegistries; - } - @Override public void onApplicationEvent(ContextClosedEvent event) { - for (MeterRegistry meterRegistry : this.meterRegistries) { - if (!meterRegistry.isClosed()) { - meterRegistry.close(); + if (event.getApplicationContext().getParent() == null) { + for (MeterRegistry meterRegistry : event.getApplicationContext() + .getBeansOfType(MeterRegistry.class) + .values()) { + if (!meterRegistry.isClosed()) { + meterRegistry.close(); + } } } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java index aa9fa98ee43..33394dedd10 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java @@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.annotation.Order; import org.springframework.test.util.ReflectionTestUtils; @@ -42,6 +43,7 @@ import static org.mockito.Mockito.mock; * * @author Andy Wilkinson * @author Moritz Halbritter + * @author Michael Berry */ class MetricsAutoConfigurationTests { @@ -89,6 +91,20 @@ class MetricsAutoConfigurationTests { }); } + @Test + void meterRegistryCloserShouldNotCloseOnNonRootContextClosing() { + this.contextRunner.withUserConfiguration(MeterRegistryConfiguration.class).run((context) -> { + MeterRegistry meterRegistry = context.getBean(MeterRegistry.class); + GenericApplicationContext childContext = new GenericApplicationContext(); + childContext.setParent(context); + childContext.refresh(); + childContext.close(); + assertThat(meterRegistry.isClosed()).isFalse(); + context.close(); + assertThat(meterRegistry.isClosed()).isTrue(); + }); + } + @Configuration(proxyBeanMethods = false) static class CustomClockConfiguration { From e65baba3a7f327bc17d05225953b6525902df2bb Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 28 Nov 2025 09:24:49 +0000 Subject: [PATCH 2/2] Polish "Stop closing parent's MeterRegistry beans when child context closes" See gh-48319 --- .../metrics/MetricsAutoConfiguration.java | 19 +++++++++----- .../MetricsAutoConfigurationTests.java | 25 +++++++++++-------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java index 9da104ab807..8aba3d98311 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java @@ -70,8 +70,8 @@ public class MetricsAutoConfiguration { } @Bean - MeterRegistryCloser meterRegistryCloser() { - return new MeterRegistryCloser(); + MeterRegistryCloser meterRegistryCloser(ApplicationContext context) { + return new MeterRegistryCloser(context); } /** @@ -80,12 +80,19 @@ public class MetricsAutoConfiguration { */ static class MeterRegistryCloser implements ApplicationListener { + private final ApplicationContext context; + + private final Iterable meterRegistries; + + MeterRegistryCloser(ApplicationContext context) { + this.meterRegistries = context.getBeansOfType(MeterRegistry.class).values(); + this.context = context; + } + @Override public void onApplicationEvent(ContextClosedEvent event) { - if (event.getApplicationContext().getParent() == null) { - for (MeterRegistry meterRegistry : event.getApplicationContext() - .getBeansOfType(MeterRegistry.class) - .values()) { + if (this.context.equals(event.getApplicationContext())) { + for (MeterRegistry meterRegistry : this.meterRegistries) { if (!meterRegistry.isClosed()) { meterRegistry.close(); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java index 33394dedd10..000860e2214 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java @@ -30,7 +30,6 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.annotation.Order; import org.springframework.test.util.ReflectionTestUtils; @@ -92,17 +91,21 @@ class MetricsAutoConfigurationTests { } @Test - void meterRegistryCloserShouldNotCloseOnNonRootContextClosing() { - this.contextRunner.withUserConfiguration(MeterRegistryConfiguration.class).run((context) -> { - MeterRegistry meterRegistry = context.getBean(MeterRegistry.class); - GenericApplicationContext childContext = new GenericApplicationContext(); - childContext.setParent(context); - childContext.refresh(); - childContext.close(); - assertThat(meterRegistry.isClosed()).isFalse(); - context.close(); - assertThat(meterRegistry.isClosed()).isTrue(); + void meterRegistryCloserShouldOnlyCloseRegistriesBelongingToContextBeingClosed() { + MeterRegistry parentMeterRegistry = new SimpleMeterRegistry(); + MeterRegistry childMeterRegistry = new SimpleMeterRegistry(); + this.contextRunner.withBean(MeterRegistry.class, () -> parentMeterRegistry).run((parent) -> { + this.contextRunner.withBean(MeterRegistry.class, () -> childMeterRegistry) + .withParent(parent) + .run((child) -> { + assertThat(childMeterRegistry.isClosed()).isFalse(); + assertThat(parentMeterRegistry.isClosed()).isFalse(); + }); + assertThat(childMeterRegistry.isClosed()).isTrue(); + assertThat(parentMeterRegistry.isClosed()).isFalse(); }); + assertThat(childMeterRegistry.isClosed()).isTrue(); + assertThat(parentMeterRegistry.isClosed()).isTrue(); } @Configuration(proxyBeanMethods = false)