Skip to content

Commit e237390

Browse files
committed
Ignore prototype DataSource beans for metrics and health
Previously, if a prototype DataSource bean was defined, Actuator's metrics and health would try to access an instance of it. At best this was wasteful as the new instance would only be used for metrics and health and would not be indicative of the app's DataSource usage. At worst, it could cause a failure in the unusual case of the prototype bean definition requiring arguments to be supplied using ObjectProvider.getObject(Object...) or the like. This commit address the problem by ignoring prototype DataSource for metrics and health. Other types of beans for which Actuator provides metrics and health are similarly affected. They have not be fixed here as the situation is so unusual. Should another problem arise in the future, it can be addressed at that time when there's a clear need. Closes gh-44706
1 parent 4dea971 commit e237390

File tree

4 files changed

+77
-5
lines changed

4 files changed

+77
-5
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public void afterPropertiesSet() {
8989
public HealthContributor dbHealthContributor(ConfigurableListableBeanFactory beanFactory,
9090
DataSourceHealthIndicatorProperties dataSourceHealthIndicatorProperties) {
9191
Map<String, DataSource> dataSources = SimpleAutowireCandidateResolver.resolveAutowireCandidates(beanFactory,
92-
DataSource.class);
92+
DataSource.class, false, true);
9393
if (dataSourceHealthIndicatorProperties.isIgnoreRoutingDataSources()) {
9494
Map<String, DataSource> filteredDatasources = dataSources.entrySet()
9595
.stream()

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,8 @@ static class DataSourcePoolMetadataMetricsConfiguration {
7272
@Bean
7373
DataSourcePoolMetadataMeterBinder dataSourcePoolMetadataMeterBinder(ConfigurableListableBeanFactory beanFactory,
7474
ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
75-
return new DataSourcePoolMetadataMeterBinder(
76-
SimpleAutowireCandidateResolver.resolveAutowireCandidates(beanFactory, DataSource.class),
77-
metadataProviders);
75+
return new DataSourcePoolMetadataMeterBinder(SimpleAutowireCandidateResolver
76+
.resolveAutowireCandidates(beanFactory, DataSource.class, false, true), metadataProviders);
7877
}
7978

8079
static class DataSourcePoolMetadataMeterBinder implements MeterBinder {
@@ -141,7 +140,7 @@ static class HikariDataSourceMeterBinder implements MeterBinder {
141140

142141
@Override
143142
public void bindTo(MeterRegistry registry) {
144-
this.dataSources.stream(ObjectProvider.UNFILTERED).forEach((dataSource) -> {
143+
this.dataSources.stream(ObjectProvider.UNFILTERED, false).forEach((dataSource) -> {
145144
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class,
146145
HikariDataSource.class);
147146
if (hikariDataSource != null) {

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java

+36
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@
1919
import java.sql.SQLException;
2020
import java.util.HashMap;
2121
import java.util.Map;
22+
import java.util.UUID;
2223

2324
import javax.sql.DataSource;
2425

26+
import com.zaxxer.hikari.HikariDataSource;
2527
import org.junit.jupiter.api.Test;
2628

2729
import org.springframework.beans.BeansException;
30+
import org.springframework.beans.factory.config.BeanDefinition;
2831
import org.springframework.beans.factory.config.BeanPostProcessor;
2932
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
3033
import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration.RoutingDataSourceHealthContributor;
@@ -41,6 +44,7 @@
4144
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
4245
import org.springframework.context.annotation.Bean;
4346
import org.springframework.context.annotation.Configuration;
47+
import org.springframework.context.annotation.Scope;
4448
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
4549

4650
import static org.assertj.core.api.Assertions.assertThat;
@@ -213,6 +217,16 @@ void runWhenDataSourceHasNullRoutingKeyShouldProduceUnnamedComposedIndicator() {
213217
});
214218
}
215219

220+
@Test
221+
void prototypeDataSourceIsIgnored() {
222+
this.contextRunner
223+
.withUserConfiguration(EmbeddedDataSourceConfiguration.class, PrototypeDataSourceConfiguration.class)
224+
.run((context) -> {
225+
assertThat(context).doesNotHaveBean(CompositeHealthContributor.class);
226+
assertThat(context.getBeansOfType(DataSourceHealthIndicator.class)).hasSize(1);
227+
});
228+
}
229+
216230
@Configuration(proxyBeanMethods = false)
217231
@EnableConfigurationProperties
218232
static class DataSourceConfig {
@@ -317,4 +331,26 @@ AbstractRoutingDataSource routingDataSource() throws Exception {
317331

318332
}
319333

334+
@Configuration(proxyBeanMethods = false)
335+
static class PrototypeDataSourceConfiguration {
336+
337+
@Bean
338+
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
339+
DataSource dataSourcePrototype(String username, String password) {
340+
return createHikariDataSource(username, password);
341+
}
342+
343+
private HikariDataSource createHikariDataSource(String username, String password) {
344+
String url = "jdbc:hsqldb:mem:test-" + UUID.randomUUID();
345+
HikariDataSource hikariDataSource = DataSourceBuilder.create()
346+
.url(url)
347+
.type(HikariDataSource.class)
348+
.username(username)
349+
.password(password)
350+
.build();
351+
return hikariDataSource;
352+
}
353+
354+
}
355+
320356
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfigurationTests.java

+37
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.junit.jupiter.api.Test;
2929

3030
import org.springframework.aop.framework.ProxyFactory;
31+
import org.springframework.beans.factory.config.BeanDefinition;
3132
import org.springframework.beans.factory.config.BeanPostProcessor;
3233
import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor;
3334
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
@@ -39,6 +40,7 @@
3940
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
4041
import org.springframework.context.annotation.Bean;
4142
import org.springframework.context.annotation.Configuration;
43+
import org.springframework.context.annotation.Scope;
4244
import org.springframework.core.Ordered;
4345
import org.springframework.core.PriorityOrdered;
4446
import org.springframework.jdbc.datasource.DelegatingDataSource;
@@ -222,6 +224,19 @@ void hikariDataSourceIsInstrumentedWithoutMetadataProvider() {
222224
});
223225
}
224226

227+
@Test
228+
void prototypeDataSourceIsIgnored() {
229+
this.contextRunner
230+
.withUserConfiguration(OneHikariDataSourceConfiguration.class, PrototypeDataSourceConfiguration.class)
231+
.run((context) -> {
232+
context.getBean("hikariDataSource", DataSource.class).getConnection();
233+
((DataSource) context.getBean("prototypeDataSource", "", "")).getConnection();
234+
MeterRegistry registry = context.getBean(MeterRegistry.class);
235+
assertThat(registry.get("hikaricp.connections").meter().getId().getTags())
236+
.containsExactly(Tag.of("pool", "hikariDataSource"));
237+
});
238+
}
239+
225240
private static HikariDataSource createHikariDataSource(String poolName) {
226241
String url = "jdbc:hsqldb:mem:test-" + UUID.randomUUID();
227242
HikariDataSource hikariDataSource = DataSourceBuilder.create().url(url).type(HikariDataSource.class).build();
@@ -334,6 +349,28 @@ private org.apache.tomcat.jdbc.pool.DataSource createTomcatDataSource() {
334349

335350
}
336351

352+
@Configuration(proxyBeanMethods = false)
353+
static class PrototypeDataSourceConfiguration {
354+
355+
@Bean
356+
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
357+
DataSource prototypeDataSource(String username, String password) {
358+
return createHikariDataSource(username, password);
359+
}
360+
361+
private HikariDataSource createHikariDataSource(String username, String password) {
362+
String url = "jdbc:hsqldb:mem:test-" + UUID.randomUUID();
363+
HikariDataSource hikariDataSource = DataSourceBuilder.create()
364+
.url(url)
365+
.type(HikariDataSource.class)
366+
.username(username)
367+
.password(password)
368+
.build();
369+
return hikariDataSource;
370+
}
371+
372+
}
373+
337374
@Configuration(proxyBeanMethods = false)
338375
static class HikariSealingConfiguration {
339376

0 commit comments

Comments
 (0)