I'm using a MeterBinder
as suggested by the docs to register a gauge.
While this worked perfectly fine on Spring Boot 2.4.9 it results in a bean definition cycle with 2.5.3,
Minimal project to reproduce the issue: https://github.com/joshiste/issues/tree/boot-meterbinder
the dependencies of some of the beans in the application context form a cycle:
┌─────┐
| service defined in com.example.demo.DemoApplication
↑ ↓
| usersRepository defined in com.example.demo.UsersRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration
↑ ↓
| metricsRepositoryMethodInvocationListener defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/data/RepositoryMetricsAutoConfiguration.class]
↑ ↓
| simpleMeterRegistry defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleMetricsExportAutoConfiguration.class]
↑ ↓
| userMetrics defined in com.example.demo.DemoApplication
└─────┘
My Bean definitions:
@Bean
UserService service(UsersRepository repsitory) {
return new UserService(repsitory);
}
@Bean
MeterBinder userMetrics(UserService userService) {
return registry -> Gauge.builder("user.cout", userService::count);
}
```
**Comment From: wilkinsona**
Thanks for the report, @joshiste. The cycle is due to the introduction of [metrics for Spring Data repositories](https://github.com/spring-projects/spring-boot/issues/22217). You can break it in your own code by making `UserService` lazy when you inject it into `userMetrics`:
```java
@Bean
MeterBinder userMetrics(@Lazy UserService userService) {
return registry -> Gauge.builder("user.cout", userService::count);
}
We'll have to see if there's something we can do in Spring Boot to make this unnecessary. If there isn't, we can at least update the documentation with some additional guidance.
Comment From: wilkinsona
We can break the cycle in Boot by annotating the MeterRegistry
that's injected into the metricsRepositoryMethodInvocationListener
@Bean
method @Lazy
. Another option may be to use ObjectFactory
to defer the retrieval of the bean but this would require changes to MetricsRepositoryMethodInvocationListener
to overload the constructor. I'd like to discuss the options with the team.
Comment From: philwebb
We discussed this today and we're going to change the MetricsRepositoryMethodInvocationListener
to accept a Supplier
. We'll deprecate the existing constructor and call the new one with a SingletonSupplier
.