Break GaugeWriter dependency cycle in MetricExportAutoConfiguration

Previously, MetricExportAutoConfiguration consumed
ExportMetricWriter-annotated GaugeWriter beans in its constructor and
also produced such a bean from one of its @Bean methods. This cycle
caused a BeanCurrentlyInCreationException to be thrown when the
bean method was active (the spring.metrics.export.statsd.host property
was set).

This commit break the cycle by moving the bean method into a separate,
nested configuration class. It also updates the existing test for
auto-configuration of a Statsd writer to catch any possible cycles
and to verify that the writer has be registered with the
MetricsExporter.

Closes gh-6544
pull/6588/head
Andy Wilkinson 8 years ago
parent 899b851c6f
commit 22ac6bcb2d

@ -55,8 +55,6 @@ import org.springframework.util.CollectionUtils;
@EnableConfigurationProperties
public class MetricExportAutoConfiguration {
private final MetricExportProperties properties;
private final MetricsEndpointMetricReader endpointReader;
private final List<MetricReader> readers;
@ -70,7 +68,6 @@ public class MetricExportAutoConfiguration {
@ExportMetricReader ObjectProvider<List<MetricReader>> readersProvider,
@ExportMetricWriter ObjectProvider<Map<String, GaugeWriter>> writersProvider,
ObjectProvider<Map<String, Exporter>> exportersProvider) {
this.properties = properties;
this.endpointReader = endpointReaderProvider.getIfAvailable();
this.readers = readersProvider.getIfAvailable();
this.writers = writersProvider.getIfAvailable();
@ -79,7 +76,8 @@ public class MetricExportAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "metricWritersMetricExporter")
public SchedulingConfigurer metricWritersMetricExporter() {
public SchedulingConfigurer metricWritersMetricExporter(
MetricExportProperties properties) {
Map<String, GaugeWriter> writers = new HashMap<String, GaugeWriter>();
MetricReader reader = this.endpointReader;
if (reader == null && !CollectionUtils.isEmpty(this.readers)) {
@ -89,7 +87,7 @@ public class MetricExportAutoConfiguration {
if (reader == null && CollectionUtils.isEmpty(this.exporters)) {
return new NoOpSchedulingConfigurer();
}
MetricExporters exporters = new MetricExporters(this.properties);
MetricExporters exporters = new MetricExporters(properties);
if (reader != null) {
if (!CollectionUtils.isEmpty(this.writers)) {
writers.putAll(this.writers);
@ -102,14 +100,19 @@ public class MetricExportAutoConfiguration {
return exporters;
}
@Bean
@ExportMetricWriter
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.metrics.export.statsd", name = "host")
public StatsdMetricWriter statsdMetricWriter() {
MetricExportProperties.Statsd statsdProperties = this.properties.getStatsd();
return new StatsdMetricWriter(statsdProperties.getPrefix(),
statsdProperties.getHost(), statsdProperties.getPort());
@Configuration
static class StatsdConfiguration {
@Bean
@ExportMetricWriter
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.metrics.export.statsd", name = "host")
public StatsdMetricWriter statsdMetricWriter(MetricExportProperties properties) {
MetricExportProperties.Statsd statsdProperties = properties.getStatsd();
return new StatsdMetricWriter(statsdProperties.getPrefix(),
statsdProperties.getHost(), statsdProperties.getPort());
}
}
@Configuration

@ -16,6 +16,8 @@
package org.springframework.boot.actuate.autoconfigure;
import java.util.Map;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
@ -30,6 +32,7 @@ import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.export.MetricCopyExporter;
import org.springframework.boot.actuate.metrics.export.MetricExporters;
import org.springframework.boot.actuate.metrics.statsd.StatsdMetricWriter;
import org.springframework.boot.actuate.metrics.writer.GaugeWriter;
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils;
@ -41,6 +44,8 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
@ -137,16 +142,24 @@ public class MetricExportAutoConfigurationTests {
this.context.getBean(StatsdMetricWriter.class);
}
@SuppressWarnings("unchecked")
@Test
public void statsdWithHost() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.metrics.export.statsd.host=localhost");
this.context.register(WriterConfig.class, MetricEndpointConfiguration.class,
this.context.register(MetricEndpointConfiguration.class,
MetricExportAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBean(StatsdMetricWriter.class)).isNotNull();
StatsdMetricWriter statsdWriter = this.context.getBean(StatsdMetricWriter.class);
assertThat(statsdWriter).isNotNull();
SchedulingConfigurer schedulingConfigurer = this.context
.getBean(SchedulingConfigurer.class);
Map<String, GaugeWriter> exporters = (Map<String, GaugeWriter>) ReflectionTestUtils
.getField(schedulingConfigurer, "writers");
assertThat(exporters).containsValue(statsdWriter);
}
@Configuration

Loading…
Cancel
Save