From 8229733f0d33658a9f3d8e66a5f7ece4e468f3f4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 2 Feb 2018 15:28:02 +0000 Subject: [PATCH] Avoid triggering early init when creating MeterRegistryPostProceesor Closes gh-11890 --- .../metrics/MeterRegistryConfigurer.java | 90 +++++++++++++++++++ .../metrics/MeterRegistryPostProcessor.java | 77 +++++----------- .../metrics/MetricsAutoConfiguration.java | 15 +--- ...java => MeterRegistryConfigurerTests.java} | 73 +++++++-------- 4 files changed, 147 insertions(+), 108 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java rename spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/{MeterRegistryPostProcessorTests.java => MeterRegistryConfigurerTests.java} (57%) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java new file mode 100644 index 00000000000..7b8affefe7c --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012-2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.metrics; + +import java.util.Collection; +import java.util.Collections; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.binder.MeterBinder; +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; +import io.micrometer.core.instrument.config.MeterFilter; + +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.util.LambdaSafe; + +/** + * {@link BeanPostProcessor} to apply {@link MeterRegistryCustomizer customizers}, + * {@link MeterFilter filters}, {@link MeterBinder binders} and {@link Metrics#addRegistry + * global registration} to {@link MeterRegistry meter registries}. This post processor + * intentionally skips {@link CompositeMeterRegistry} with the assumptions that the + * registries it contains are beans and will be customized directly. + * + * @author Jon Schneider + * @author Phillip Webb + */ +class MeterRegistryConfigurer { + + private final Collection> customizers; + + private final Collection filters; + + private final Collection binders; + + private final boolean addToGlobalRegistry; + + MeterRegistryConfigurer(Collection binders, + Collection filters, + Collection> customizers, + boolean addToGlobalRegistry) { + this.binders = (binders != null ? binders : Collections.emptyList()); + this.filters = (filters != null ? filters : Collections.emptyList()); + this.customizers = (customizers != null ? customizers : Collections.emptyList()); + this.addToGlobalRegistry = addToGlobalRegistry; + } + + void configure(MeterRegistry registry) { + if (registry instanceof CompositeMeterRegistry) { + return; + } + // Customizers must be applied before binders, as they may add custom tags or + // alter timer or summary configuration. + customize(registry); + addFilters(registry); + addBinders(registry); + if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) { + Metrics.addRegistry(registry); + } + } + + @SuppressWarnings("unchecked") + private void customize(MeterRegistry registry) { + LambdaSafe.callbacks(MeterRegistryCustomizer.class, this.customizers, registry) + .withLogger(MeterRegistryConfigurer.class) + .invoke((customizer) -> customizer.customize(registry)); + } + + private void addFilters(MeterRegistry registry) { + this.filters.forEach(registry.config()::meterFilter); + } + + private void addBinders(MeterRegistry registry) { + this.binders.forEach((binder) -> binder.bindTo(registry)); + } + +} 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 232a9f67a22..0768f4ffbb3 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 @@ -17,87 +17,56 @@ package org.springframework.boot.actuate.autoconfigure.metrics; import java.util.Collection; -import java.util.Collections; import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.binder.MeterBinder; -import io.micrometer.core.instrument.composite.CompositeMeterRegistry; import io.micrometer.core.instrument.config.MeterFilter; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.boot.util.LambdaSafe; +import org.springframework.context.ApplicationContext; /** - * {@link BeanPostProcessor} to apply {@link MeterRegistryCustomizer customizers}, - * {@link MeterFilter filters}, {@link MeterBinder binders} and {@link Metrics#addRegistry - * global registration} to {@link MeterRegistry meter registries}. This post processor - * intentionally skips {@link CompositeMeterRegistry} with the assumptions that the - * registries it contains are beans and will be customized directly. + * {@link BeanPostProcessor} that delegates to a lazily created + * {@link MeterRegistryConfigurer} to post-process {@link MeterRegistry} beans. * * @author Jon Schneider * @author Phillip Webb + * @author Andy Wilkinson */ class MeterRegistryPostProcessor implements BeanPostProcessor { - private final Collection> customizers; + private final ApplicationContext context; - private final Collection filters; + private volatile MeterRegistryConfigurer configurer; - private final Collection binders; - - private final boolean addToGlobalRegistry; - - MeterRegistryPostProcessor(Collection binders, - Collection filters, - Collection> customizers, - boolean addToGlobalRegistry) { - this.binders = (binders != null ? binders : Collections.emptyList()); - this.filters = (filters != null ? filters : Collections.emptyList()); - this.customizers = (customizers != null ? customizers : Collections.emptyList()); - this.addToGlobalRegistry = addToGlobalRegistry; + MeterRegistryPostProcessor(ApplicationContext context) { + this.context = context; } @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) { - return bean; - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) { + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { if (bean instanceof MeterRegistry) { - postProcess((MeterRegistry) bean); + getConfigurer().configure((MeterRegistry) bean); } return bean; } - private void postProcess(MeterRegistry registry) { - if (registry instanceof CompositeMeterRegistry) { - return; - } - // Customizers must be applied before binders, as they may add custom tags or - // alter timer or summary configuration. - customize(registry); - addFilters(registry); - addBinders(registry); - if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) { - Metrics.addRegistry(registry); - } - } - @SuppressWarnings("unchecked") - private void customize(MeterRegistry registry) { - LambdaSafe.callbacks(MeterRegistryCustomizer.class, this.customizers, registry) - .withLogger(MeterRegistryPostProcessor.class) - .invoke((customizer) -> customizer.customize(registry)); - } - - private void addFilters(MeterRegistry registry) { - this.filters.forEach(registry.config()::meterFilter); + private MeterRegistryConfigurer getConfigurer() { + if (this.configurer == null) { + this.configurer = new MeterRegistryConfigurer(beansOfType(MeterBinder.class), + beansOfType(MeterFilter.class), + (Collection>) (Object) beansOfType( + MeterRegistryCustomizer.class), + this.context.getBean(MetricsProperties.class).isUseGlobalRegistry()); + } + return this.configurer; } - private void addBinders(MeterRegistry registry) { - this.binders.forEach((binder) -> binder.bindTo(registry)); + private Collection beansOfType(Class type) { + return this.context.getBeansOfType(type).values(); } } 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 8fb56173a87..fc590a9c10c 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,13 +16,8 @@ package org.springframework.boot.actuate.autoconfigure.metrics; -import java.util.Collection; - import io.micrometer.core.annotation.Timed; -import io.micrometer.core.instrument.binder.MeterBinder; -import io.micrometer.core.instrument.config.MeterFilter; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.metrics.amqp.RabbitMetricsConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.cache.CacheMetricsConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsConfiguration; @@ -40,6 +35,7 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -72,13 +68,8 @@ public class MetricsAutoConfiguration { @Bean public static MeterRegistryPostProcessor meterRegistryPostProcessor( - ObjectProvider> binders, - ObjectProvider> filters, - ObjectProvider>> customizers, - MetricsProperties properties) { - return new MeterRegistryPostProcessor(binders.getIfAvailable(), - filters.getIfAvailable(), customizers.getIfAvailable(), - properties.isUseGlobalRegistry()); + ApplicationContext context) { + return new MeterRegistryPostProcessor(context); } @Bean 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/MeterRegistryConfigurerTests.java similarity index 57% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessorTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurerTests.java index b44c68b01a0..76951880a2e 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/MeterRegistryConfigurerTests.java @@ -38,11 +38,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; /** - * Tests for {@link MeterRegistryPostProcessor}. + * Tests for {@link MeterRegistryConfigurer}. * * @author Phillip Webb + * @author Andy Wilkinson */ -public class MeterRegistryPostProcessorTests { +public class MeterRegistryConfigurerTests { private List binders = new ArrayList<>(); @@ -72,62 +73,50 @@ public class MeterRegistryPostProcessorTests { } @Test - public void postProcessWhenNotRegistryShouldReturnBean() { - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( - this.binders, this.filters, this.customizers, false); - Object bean = new Object(); - String beanName = "name"; - assertThat(processor.postProcessBeforeInitialization(bean, beanName)) - .isEqualTo(bean); - assertThat(processor.postProcessAfterInitialization(bean, beanName)) - .isEqualTo(bean); - } - - @Test - public void postProcessorWhenCompositeShouldSkip() { + public void configureWhenCompositeShouldSkip() { this.binders.add(this.mockBinder); this.customizers.add(this.mockCustomizer); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( - this.binders, this.filters, this.customizers, false); - processor.postProcessAfterInitialization(new CompositeMeterRegistry(), "name"); + MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders, + this.filters, this.customizers, false); + configurer.configure(new CompositeMeterRegistry()); verifyZeroInteractions(this.mockBinder, this.mockCustomizer); } @Test - public void postProcessShouldApplyCustomizer() { + public void configureShouldApplyCustomizer() { this.customizers.add(this.mockCustomizer); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( - this.binders, this.filters, this.customizers, false); - processor.postProcessAfterInitialization(this.mockRegistry, "name"); + MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders, + this.filters, this.customizers, false); + configurer.configure(this.mockRegistry); verify(this.mockCustomizer).customize(this.mockRegistry); } @Test - public void postProcessShouldApplyFilter() { + public void configureShouldApplyFilter() { this.filters.add(this.mockFilter); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( - this.binders, this.filters, this.customizers, false); - processor.postProcessAfterInitialization(this.mockRegistry, "name"); + MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders, + this.filters, this.customizers, false); + configurer.configure(this.mockRegistry); verify(this.mockConfig).meterFilter(this.mockFilter); } @Test - public void postProcessShouldApplyBinder() { + public void configureShouldApplyBinder() { this.binders.add(this.mockBinder); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( - this.binders, this.filters, this.customizers, false); - processor.postProcessAfterInitialization(this.mockRegistry, "name"); + MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders, + this.filters, this.customizers, false); + configurer.configure(this.mockRegistry); verify(this.mockBinder).bindTo(this.mockRegistry); } @Test - public void postProcessShouldBeCallInOrderCustomizeFilterBinder() { + public void configureShouldBeCalledInOrderCustomizerFilterBinder() { this.customizers.add(this.mockCustomizer); this.filters.add(this.mockFilter); this.binders.add(this.mockBinder); - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( - this.binders, this.filters, this.customizers, false); - processor.postProcessAfterInitialization(this.mockRegistry, "name"); + MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders, + this.filters, this.customizers, false); + configurer.configure(this.mockRegistry); InOrder ordered = inOrder(this.mockBinder, this.mockConfig, this.mockCustomizer); ordered.verify(this.mockCustomizer).customize(this.mockRegistry); ordered.verify(this.mockConfig).meterFilter(this.mockFilter); @@ -135,11 +124,11 @@ public class MeterRegistryPostProcessorTests { } @Test - public void postProcessWhenAddToGlobalRegistryShouldAddToGlobalRegistry() { - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( - this.binders, this.filters, this.customizers, true); + public void configureWhenAddToGlobalRegistryShouldAddToGlobalRegistry() { + MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders, + this.filters, this.customizers, true); try { - processor.postProcessAfterInitialization(this.mockRegistry, "name"); + configurer.configure(this.mockRegistry); assertThat(Metrics.globalRegistry.getRegistries()) .contains(this.mockRegistry); } @@ -149,10 +138,10 @@ public class MeterRegistryPostProcessorTests { } @Test - public void postProcessWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() { - MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( - this.binders, this.filters, this.customizers, false); - processor.postProcessAfterInitialization(this.mockRegistry, "name"); + public void configureWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() { + MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders, + this.filters, this.customizers, false); + configurer.configure(this.mockRegistry); assertThat(Metrics.globalRegistry.getRegistries()) .doesNotContain(this.mockRegistry); }