From fe72f307f4f3e94c79e253f223ba3d5d4ef1def3 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 11 Nov 2022 16:31:21 +0000 Subject: [PATCH] Avoid actuator beans being ineligible for post-processing Closes gh-33110 --- ...ndpointManagementContextConfiguration.java | 15 ++-- .../metrics/MeterRegistryPostProcessor.java | 17 ++--- .../metrics/MetricsAutoConfiguration.java | 3 +- .../MeterRegistryPostProcessorTests.java | 73 +++++++++++-------- 4 files changed, 64 insertions(+), 44 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java index 76d3cfb24b60..1f65df0cb040 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java @@ -21,10 +21,12 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; @@ -67,6 +69,7 @@ import org.springframework.http.codec.json.Jackson2JsonEncoder; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.util.StringUtils; +import org.springframework.util.function.SingletonSupplier; import org.springframework.web.reactive.DispatcherHandler; /** @@ -134,8 +137,9 @@ public ControllerEndpointHandlerMapping controllerEndpointHandlerMapping( @ConditionalOnBean(EndpointObjectMapper.class) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) static ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor serverCodecConfigurerEndpointObjectMapperBeanPostProcessor( - EndpointObjectMapper endpointObjectMapper) { - return new ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor(endpointObjectMapper); + ObjectProvider endpointObjectMapper) { + return new ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor( + SingletonSupplier.of(endpointObjectMapper::getObject)); } /** @@ -147,9 +151,10 @@ static class ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor implemen private static final List MEDIA_TYPES = Collections .unmodifiableList(Arrays.asList(MediaType.APPLICATION_JSON, new MediaType("application", "*+json"))); - private final EndpointObjectMapper endpointObjectMapper; + private final Supplier endpointObjectMapper; - ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor(EndpointObjectMapper endpointObjectMapper) { + ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor( + Supplier endpointObjectMapper) { this.endpointObjectMapper = endpointObjectMapper; } @@ -173,7 +178,7 @@ private void process(Encoder encoder) { if (encoder instanceof Jackson2JsonEncoder) { Jackson2JsonEncoder jackson2JsonEncoder = (Jackson2JsonEncoder) encoder; jackson2JsonEncoder.registerObjectMappersForType(OperationResponseBody.class, (associations) -> { - ObjectMapper objectMapper = this.endpointObjectMapper.get(); + ObjectMapper objectMapper = this.endpointObjectMapper.get().get(); MEDIA_TYPES.forEach((mimeType) -> associations.put(mimeType, objectMapper)); }); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessor.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessor.java index d5e279b1c2f3..b554a4b12b05 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessor.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessor.java @@ -44,7 +44,7 @@ class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializing private final boolean hasNoCompositeMeterRegistryBeans; - private final boolean useGlobalRegistry; + private final ObjectProvider properties; private final ObjectProvider> customizers; @@ -56,22 +56,21 @@ class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializing private final Set deferredBindings = new LinkedHashSet<>(); - MeterRegistryPostProcessor(ApplicationContext applicationContext, MetricsProperties metricsProperties, - ObjectProvider> customizers, ObjectProvider filters, - ObjectProvider binders) { - this(hasNoCompositeMeterRegistryBeans(applicationContext), metricsProperties.isUseGlobalRegistry(), customizers, - filters, binders); + MeterRegistryPostProcessor(ApplicationContext applicationContext, + ObjectProvider metricsProperties, ObjectProvider> customizers, + ObjectProvider filters, ObjectProvider binders) { + this(hasNoCompositeMeterRegistryBeans(applicationContext), metricsProperties, customizers, filters, binders); } private static boolean hasNoCompositeMeterRegistryBeans(ApplicationContext applicationContext) { return applicationContext.getBeanNamesForType(CompositeMeterRegistry.class, false, false).length == 0; } - MeterRegistryPostProcessor(boolean hasNoCompositeMeterRegistryBeans, boolean useGlobalRegistry, + MeterRegistryPostProcessor(boolean hasNoCompositeMeterRegistryBeans, ObjectProvider properties, ObjectProvider> customizers, ObjectProvider filters, ObjectProvider binders) { this.hasNoCompositeMeterRegistryBeans = hasNoCompositeMeterRegistryBeans; - this.useGlobalRegistry = useGlobalRegistry; + this.properties = properties; this.customizers = customizers; this.filters = filters; this.binders = binders; @@ -121,7 +120,7 @@ private void applyFilters(MeterRegistry meterRegistry) { } private void addToGlobalRegistryIfNecessary(MeterRegistry meterRegistry) { - if (this.useGlobalRegistry && !isGlobalRegistry(meterRegistry)) { + if (this.properties.getObject().isUseGlobalRegistry() && !isGlobalRegistry(meterRegistry)) { Metrics.addRegistry(meterRegistry); } } 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 3cc668e257e3..16eaab791d98 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 @@ -51,7 +51,8 @@ public Clock micrometerClock() { @Bean public static MeterRegistryPostProcessor meterRegistryPostProcessor(ApplicationContext applicationContext, - MetricsProperties metricsProperties, ObjectProvider> meterRegistryCustomizers, + ObjectProvider metricsProperties, + ObjectProvider> meterRegistryCustomizers, ObjectProvider meterFilters, ObjectProvider meterBinders) { return new MeterRegistryPostProcessor(applicationContext, metricsProperties, meterRegistryCustomizers, meterFilters, meterBinders); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessorTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessorTests.java index dc3166a2599f..a6ee9c57a808 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessorTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessorTests.java @@ -48,6 +48,8 @@ @ExtendWith(MockitoExtension.class) class MeterRegistryPostProcessorTests { + private final MetricsProperties properties = new MetricsProperties(); + private List> customizers = new ArrayList<>(); private List filters = new ArrayList<>(); @@ -69,12 +71,16 @@ class MeterRegistryPostProcessorTests { @Mock private Config mockConfig; + MeterRegistryPostProcessorTests() { + this.properties.setUseGlobalRegistry(false); + } + @Test void postProcessAndInitializeWhenCompositeAppliesCustomizer() { this.customizers.add(this.mockCustomizer); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false, false, - createObjectProvider(this.customizers), createObjectProvider(this.filters), - createObjectProvider(this.binders)); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders)); CompositeMeterRegistry composite = new CompositeMeterRegistry(); postProcessAndInitialize(processor, composite); then(this.mockCustomizer).should().customize(composite); @@ -84,9 +90,9 @@ void postProcessAndInitializeWhenCompositeAppliesCustomizer() { void postProcessAndInitializeAppliesCustomizer() { given(this.mockRegistry.config()).willReturn(this.mockConfig); this.customizers.add(this.mockCustomizer); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false, - createObjectProvider(this.customizers), createObjectProvider(this.filters), - createObjectProvider(this.binders)); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders)); postProcessAndInitialize(processor, this.mockRegistry); then(this.mockCustomizer).should().customize(this.mockRegistry); } @@ -95,9 +101,9 @@ void postProcessAndInitializeAppliesCustomizer() { void postProcessAndInitializeAppliesFilter() { given(this.mockRegistry.config()).willReturn(this.mockConfig); this.filters.add(this.mockFilter); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false, - createObjectProvider(this.customizers), createObjectProvider(this.filters), - createObjectProvider(this.binders)); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders)); postProcessAndInitialize(processor, this.mockRegistry); then(this.mockConfig).should().meterFilter(this.mockFilter); } @@ -106,9 +112,9 @@ void postProcessAndInitializeAppliesFilter() { void postProcessAndInitializeBindsTo() { given(this.mockRegistry.config()).willReturn(this.mockConfig); this.binders.add(this.mockBinder); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false, - createObjectProvider(this.customizers), createObjectProvider(this.filters), - createObjectProvider(this.binders)); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders)); postProcessAndInitialize(processor, this.mockRegistry); then(this.mockBinder).should().bindTo(this.mockRegistry); } @@ -116,9 +122,9 @@ void postProcessAndInitializeBindsTo() { @Test void postProcessAndInitializeWhenCompositeBindsTo() { this.binders.add(this.mockBinder); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false, false, - createObjectProvider(this.customizers), createObjectProvider(this.filters), - createObjectProvider(this.binders)); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders)); CompositeMeterRegistry composite = new CompositeMeterRegistry(); postProcessAndInitialize(processor, composite); then(this.mockBinder).should().bindTo(composite); @@ -127,8 +133,9 @@ void postProcessAndInitializeWhenCompositeBindsTo() { @Test void postProcessAndInitializeWhenCompositeExistsDoesNotBindTo() { given(this.mockRegistry.config()).willReturn(this.mockConfig); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false, false, - createObjectProvider(this.customizers), createObjectProvider(this.filters), null); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), null); postProcessAndInitialize(processor, this.mockRegistry); then(this.mockBinder).shouldHaveNoInteractions(); } @@ -139,9 +146,9 @@ void postProcessAndInitializeBeOrderedCustomizerThenFilterThenBindTo() { this.customizers.add(this.mockCustomizer); this.filters.add(this.mockFilter); this.binders.add(this.mockBinder); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false, - createObjectProvider(this.customizers), createObjectProvider(this.filters), - createObjectProvider(this.binders)); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders)); postProcessAndInitialize(processor, this.mockRegistry); InOrder ordered = inOrder(this.mockBinder, this.mockConfig, this.mockCustomizer); then(this.mockCustomizer).should(ordered).customize(this.mockRegistry); @@ -152,9 +159,10 @@ void postProcessAndInitializeBeOrderedCustomizerThenFilterThenBindTo() { @Test void postProcessAndInitializeWhenUseGlobalRegistryTrueAddsToGlobalRegistry() { given(this.mockRegistry.config()).willReturn(this.mockConfig); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, true, - createObjectProvider(this.customizers), createObjectProvider(this.filters), - createObjectProvider(this.binders)); + this.properties.setUseGlobalRegistry(true); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders)); try { postProcessAndInitialize(processor, this.mockRegistry); assertThat(Metrics.globalRegistry.getRegistries()).contains(this.mockRegistry); @@ -167,9 +175,9 @@ void postProcessAndInitializeWhenUseGlobalRegistryTrueAddsToGlobalRegistry() { @Test void postProcessAndInitializeWhenUseGlobalRegistryFalseDoesNotAddToGlobalRegistry() { given(this.mockRegistry.config()).willReturn(this.mockConfig); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false, - createObjectProvider(this.customizers), createObjectProvider(this.filters), - createObjectProvider(this.binders)); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders)); postProcessAndInitialize(processor, this.mockRegistry); assertThat(Metrics.globalRegistry.getRegistries()).doesNotContain(this.mockRegistry); } @@ -178,9 +186,9 @@ void postProcessAndInitializeWhenUseGlobalRegistryFalseDoesNotAddToGlobalRegistr void postProcessDoesNotBindToUntilSingletonsInitialized() { given(this.mockRegistry.config()).willReturn(this.mockConfig); this.binders.add(this.mockBinder); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false, - createObjectProvider(this.customizers), createObjectProvider(this.filters), - createObjectProvider(this.binders)); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders)); processor.postProcessAfterInitialization(this.mockRegistry, "meterRegistry"); then(this.mockBinder).shouldHaveNoInteractions(); processor.afterSingletonsInstantiated(); @@ -199,4 +207,11 @@ private ObjectProvider createObjectProvider(List objects) { return objectProvider; } + @SuppressWarnings("unchecked") + private ObjectProvider createObjectProvider(T object) { + ObjectProvider objectProvider = mock(ObjectProvider.class); + given(objectProvider.getObject()).willReturn(object); + return objectProvider; + } + }