I'm trying to understand why the following code breaks with the below exception:
@Configuration
@EnableMetrics(proxyTargetClass = true)
public class MetricsConfig {
//................
@Bean
@ExportMetricWriter
public MetricWriter statsdMetricWriter() {
return new StatsdMetricWriter("13.127.9.150",8125);
}
}
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'statsdMetricWriter' defined in class path resource [demo/MetricsConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.metrics.writer.MetricWriter]: Factory method 'statsdMetricWriter' threw exception; nested exception is java.lang.NoClassDefFoundError: com/timgroup/statsd/StatsDClientErrorHandler
while as soon as I add an annotation to statsdMetricWriter(): @ConditionalOnProperty(prefix = "statsd", name = {"host", "port"})
and provided the @ConfigurationProperties(prefix = "statsd") public class StatsdProperties{//...} exists the above Spring configuration runs fine.
What dictates to Spring machinery that new StatsdMetricWriter(String, int) shouldn't work?
Updates to address @Stéphane's confusion:
Yes, the question pertains to Spring in general as well, but I'm encountering it while using Spring Boot, hence that tag.
I'm trying to add a configuration class to an existing application to export its metrics to a statsd server, and found some relevant samples here and here based on which I'm building up my own Configuration class. Of course it's not empty, I simply copied here just the relevant pieces for brevity.
I don't necessarily want to use ConditionalOnProperty but it was part of the above samples and without it it doesn't seem to work.
The confusion on my side is, even in a minimal Configuration class like that shouldn't it be possible to say:
@Bean
@ExportMetricWriter
public MetricWriter statsdMetricWriter() {
return new StatsdMetricWriter("10.101.7.130",8125);
}
I wanted to think so, but the exception above seems to contradict my thinking. Revisiting the above links and qualifying the factory method with @ConditionalOnProperty(prefix = "statsd", name = {"host", "port"}) (without including any additional dependencies on the classpath) seems to eliminate the exception. That led to the question above. Hopefully that clarifies it a bit.
Update to the original question:
With the addition of missing dependencies as per comments below the original exception is gone but I'd like to clarify for myself how @ConditionalOnProperty operates. In my current setup there's a configuration bean:
@Configuration
@EnableMetrics(proxyTargetClass = true)
@EnableConfigurationProperties(StatsdProperties.class)
public class MetricsConfig {
@Value("${statsd.host:localhost}")
private String host = "localhost";
@Value("${statsd.port:8125}")
private int port;
@Autowired
private StatsdProperties statsdProperties;
//......................
@Bean
@ConditionalOnProperty(prefix = "statsd", name = {"host", "port"})
@ExportMetricWriter
public MetricWriter statsdMetricWriter() {
logger.info("creating statsdMetricWriter....");
return new StatsdMetricWriter(
statsdProperties.getPrefix(),
statsdProperties.getHost(),
statsdProperties.getPort()
);
}
}
and the related configuration properties bean:
@ConfigurationProperties(prefix = "statsd")
public class StatsdProperties {
@Value("${statsd.host:13.127.9.150}")
private String host;
@Value("${statsd.port:8125}")
private int port;
private String prefix;
//getters & setters
What's unclear is in its execution the @ConditionalOnProperty annotation:
how does it interpret the presence of the property - is it the value existence in the applicaiton.properties file (which seems to work) or is it the value existence as a default of an @Value annotations (which doesn't seem to be taken in the consideration in my testing)? Or something else perhaps?
what is a better practice - to have @Value inside the @Configuration classes or inside the @ConfigurationProperties classes or elsewhere?
Also, semantically, @ConditionalOnProperty seems a bit misleading, to me it implies the presence of a particular property in a class. Sounds what it really tries to assert is @ConditionalOnPropertyValue or am I not understanding something?
Thank you in advance.