diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/data/MetricsRepositoryMethodInvocationListenerBeanPostProcessor.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/data/MetricsRepositoryMethodInvocationListenerBeanPostProcessor.java index c99a9c5fb8a0..18723f90c3b2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/data/MetricsRepositoryMethodInvocationListenerBeanPostProcessor.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/data/MetricsRepositoryMethodInvocationListenerBeanPostProcessor.java @@ -16,6 +16,8 @@ package org.springframework.boot.actuate.autoconfigure.metrics.data; +import java.util.function.Supplier; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.actuate.metrics.data.MetricsRepositoryMethodInvocationListener; @@ -33,8 +35,9 @@ class MetricsRepositoryMethodInvocationListenerBeanPostProcessor implements Bean private final RepositoryFactoryCustomizer customizer; - MetricsRepositoryMethodInvocationListenerBeanPostProcessor(MetricsRepositoryMethodInvocationListener listener) { - this.customizer = (repositoryFactory) -> repositoryFactory.addInvocationListener(listener); + MetricsRepositoryMethodInvocationListenerBeanPostProcessor( + Supplier listener) { + this.customizer = new MetricsRepositoryFactoryCustomizer(listener); } @Override @@ -45,4 +48,25 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro return bean; } + private static final class MetricsRepositoryFactoryCustomizer implements RepositoryFactoryCustomizer { + + private final Supplier listenerSupplier; + + private volatile MetricsRepositoryMethodInvocationListener listener; + + private MetricsRepositoryFactoryCustomizer( + Supplier listenerSupplier) { + this.listenerSupplier = listenerSupplier; + } + + @Override + public void customize(RepositoryFactorySupport repositoryFactory) { + if (this.listener == null) { + this.listener = this.listenerSupplier.get(); + } + repositoryFactory.addInvocationListener(this.listener); + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/data/RepositoryMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/data/RepositoryMetricsAutoConfiguration.java index 708e342cd503..18347e090088 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/data/RepositoryMetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/data/RepositoryMetricsAutoConfiguration.java @@ -18,6 +18,7 @@ import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; @@ -72,9 +73,9 @@ public MetricsRepositoryMethodInvocationListener metricsRepositoryMethodInvocati @Bean public static MetricsRepositoryMethodInvocationListenerBeanPostProcessor metricsRepositoryMethodInvocationListenerBeanPostProcessor( - MetricsRepositoryMethodInvocationListener metricsRepositoryMethodInvocationListener) { + ObjectProvider metricsRepositoryMethodInvocationListener) { return new MetricsRepositoryMethodInvocationListenerBeanPostProcessor( - metricsRepositoryMethodInvocationListener); + metricsRepositoryMethodInvocationListener::getObject); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/data/MetricsRepositoryMethodInvocationListenerBeanPostProcessorTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/data/MetricsRepositoryMethodInvocationListenerBeanPostProcessorTests.java index 9161dbd98eef..a7d12e649185 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/data/MetricsRepositoryMethodInvocationListenerBeanPostProcessorTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/data/MetricsRepositoryMethodInvocationListenerBeanPostProcessorTests.java @@ -38,7 +38,7 @@ class MetricsRepositoryMethodInvocationListenerBeanPostProcessorTests { private MetricsRepositoryMethodInvocationListener listener = mock(MetricsRepositoryMethodInvocationListener.class); private MetricsRepositoryMethodInvocationListenerBeanPostProcessor postProcessor = new MetricsRepositoryMethodInvocationListenerBeanPostProcessor( - this.listener); + () -> this.listener); @Test @SuppressWarnings("rawtypes") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/data/RepositoryMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/data/RepositoryMetricsAutoConfigurationTests.java index 61a607f17f7d..bb5c75a9495d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/data/RepositoryMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/data/RepositoryMetricsAutoConfigurationTests.java @@ -24,6 +24,7 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.core.instrument.distribution.HistogramSnapshot; import org.junit.jupiter.api.Test; @@ -125,6 +126,13 @@ void timerWorksWithTimedAnnotationsWhenAutoTimeRequestsIsFalse() { }); } + @Test + void doesNotTriggerEarlyInitializationThatPreventsMeterBindersFromBindingMeters() { + this.contextRunner.withUserConfiguration(MeterBinderConfiguration.class) + .run((context) -> assertThat(context.getBean(MeterRegistry.class).find("binder.test").counter()) + .isNotNull()); + } + private MeterRegistry getInitializedMeterRegistry(AssertableApplicationContext context, Class repositoryInterface) { MetricsRepositoryMethodInvocationListener listener = context @@ -158,6 +166,16 @@ public Iterable repositoryTags(RepositoryMethodInvocation invocation) { } + @Configuration(proxyBeanMethods = false) + static class MeterBinderConfiguration { + + @Bean + MeterBinder meterBinder() { + return (registry) -> registry.counter("binder.test"); + } + + } + @Configuration(proxyBeanMethods = false) static class MetricsRepositoryMethodInvocationListenerConfiguration {