From a77def1cdf2627fa9bc6a519190614384d22c651 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 15 Feb 2018 10:08:30 -0500 Subject: [PATCH] INT-4403: Micrometer and Dynamic Components JIRA: https://jira.spring.io/browse/INT-4402 Previously, Micrometer instrumentation was only applied to components during ApplicationContext initialization. `IntegrationManagementConfigurer` is now a `BeanPostProcessor` as well as a `SmartInitializingSingleton`, but only acts as a BPP after the context is initialized. This enables Micrometer instrumentation to beans added later, either via a new `BeanDefinition` or `bf.initializeBean`. NOTE: destroying and re-creating a bean will use the same `Meters`. --- .../IntegrationManagementConfigurer.java | 44 +++++++++++++------ .../micrometer/MicrometerMetricsTests.java | 22 ++++++++++ 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagementConfigurer.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagementConfigurer.java index c0eb925734..e475b79901 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagementConfigurer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagementConfigurer.java @@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.integration.support.management.IntegrationManagement.ManagementOverrides; @@ -47,7 +48,7 @@ import org.springframework.util.StringUtils; * */ public class IntegrationManagementConfigurer implements SmartInitializingSingleton, ApplicationContextAware, - BeanNameAware { + BeanNameAware, BeanPostProcessor { private static final Log logger = LogFactory.getLog(IntegrationManagementConfigurer.class); @@ -59,6 +60,8 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet private final Map sourcesByName = new HashMap(); + private final Map sourceConfigurers = new HashMap<>(); + private ApplicationContext applicationContext; private String beanName; @@ -77,6 +80,8 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet private String[] enabledStatsPatterns = { }; + private volatile boolean singletonsInstantiated; + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; @@ -212,8 +217,7 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet if (this.metricsFactory == null) { this.metricsFactory = new DefaultMetricsFactory(); } - Map sourceConfigurers = this.applicationContext - .getBeansOfType(MessageSourceMetricsConfigurer.class); + this.sourceConfigurers.putAll(this.applicationContext.getBeansOfType(MessageSourceMetricsConfigurer.class)); Map managed = this.applicationContext.getBeansOfType(IntegrationManagement.class); for (Entry entry : managed.entrySet()) { IntegrationManagement bean = entry.getValue(); @@ -221,17 +225,31 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet bean.setLoggingEnabled(this.defaultLoggingEnabled); } String name = entry.getKey(); - if (bean instanceof MessageChannelMetrics) { - configureChannelMetrics(name, (MessageChannelMetrics) bean); - } - else if (bean instanceof MessageHandlerMetrics) { - configureHandlerMetrics(name, (MessageHandlerMetrics) bean); - } - else if (bean instanceof MessageSourceMetrics) { - configureSourceMetrics(name, (MessageSourceMetrics) bean); - sourceConfigurers.values().forEach(c -> c.configure((MessageSourceMetrics) bean, name)); - } + doConfigureMetrics(bean, name); } + this.singletonsInstantiated = true; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (this.singletonsInstantiated) { + return doConfigureMetrics(bean, beanName); + } + return bean; + } + + private Object doConfigureMetrics(Object bean, String name) { + if (bean instanceof MessageChannelMetrics) { + configureChannelMetrics(name, (MessageChannelMetrics) bean); + } + else if (bean instanceof MessageHandlerMetrics) { + configureHandlerMetrics(name, (MessageHandlerMetrics) bean); + } + else if (bean instanceof MessageSourceMetrics) { + configureSourceMetrics(name, (MessageSourceMetrics) bean); + this.sourceConfigurers.values().forEach(c -> c.configure((MessageSourceMetrics) bean, name)); + } + return bean; } @SuppressWarnings("unchecked") diff --git a/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsTests.java b/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsTests.java index 022c000b32..95f4dd7c5f 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsTests.java @@ -25,16 +25,21 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.channel.AbstractMessageChannel; import org.springframework.integration.channel.AbstractPollableChannel; +import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.config.EnableIntegration; import org.springframework.integration.config.EnableIntegrationManagement; import org.springframework.integration.core.MessageSource; import org.springframework.integration.endpoint.AbstractMessageSource; +import org.springframework.integration.test.util.TestUtils; import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; import org.springframework.messaging.PollableChannel; @@ -59,6 +64,9 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry; @DirtiesContext public class MicrometerMetricsTests { + @Autowired + private ConfigurableApplicationContext context; + @Autowired private MeterRegistry meterRegistry; @@ -146,6 +154,20 @@ public class MicrometerMetricsTests { default: } } + BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) this.context.getBeanFactory(); + beanFactory.registerBeanDefinition("newChannel", + BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class).getRawBeanDefinition()); + DirectChannel newChannel = this.context.getBean("newChannel", DirectChannel.class); + assertThat(this.meterRegistry.getMeters().size()).isEqualTo(24); + Timer timer = meterRegistry.get("newChannel.timer").timer(); + assertThat(timer).isSameAs(TestUtils.getPropertyValue(newChannel, "channelMetrics.timer")); + beanFactory.removeBeanDefinition("newChannel"); + // verify that the meter registry reuses the existing timer + beanFactory.registerBeanDefinition("newChannel", + BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class).getRawBeanDefinition()); + newChannel = this.context.getBean("newChannel", DirectChannel.class); + assertThat(this.meterRegistry.getMeters().size()).isEqualTo(24); + assertThat(timer).isSameAs(TestUtils.getPropertyValue(newChannel, "channelMetrics.timer")); } @Configuration