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
This commit is contained in:
parent
4dea97141c
commit
e237390e66
|
@ -89,7 +89,7 @@ public class DataSourceHealthContributorAutoConfiguration implements Initializin
|
|||
public HealthContributor dbHealthContributor(ConfigurableListableBeanFactory beanFactory,
|
||||
DataSourceHealthIndicatorProperties dataSourceHealthIndicatorProperties) {
|
||||
Map<String, DataSource> dataSources = SimpleAutowireCandidateResolver.resolveAutowireCandidates(beanFactory,
|
||||
DataSource.class);
|
||||
DataSource.class, false, true);
|
||||
if (dataSourceHealthIndicatorProperties.isIgnoreRoutingDataSources()) {
|
||||
Map<String, DataSource> filteredDatasources = dataSources.entrySet()
|
||||
.stream()
|
||||
|
|
|
@ -72,9 +72,8 @@ public class DataSourcePoolMetricsAutoConfiguration {
|
|||
@Bean
|
||||
DataSourcePoolMetadataMeterBinder dataSourcePoolMetadataMeterBinder(ConfigurableListableBeanFactory beanFactory,
|
||||
ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
|
||||
return new DataSourcePoolMetadataMeterBinder(
|
||||
SimpleAutowireCandidateResolver.resolveAutowireCandidates(beanFactory, DataSource.class),
|
||||
metadataProviders);
|
||||
return new DataSourcePoolMetadataMeterBinder(SimpleAutowireCandidateResolver
|
||||
.resolveAutowireCandidates(beanFactory, DataSource.class, false, true), metadataProviders);
|
||||
}
|
||||
|
||||
static class DataSourcePoolMetadataMeterBinder implements MeterBinder {
|
||||
|
@ -141,7 +140,7 @@ public class DataSourcePoolMetricsAutoConfiguration {
|
|||
|
||||
@Override
|
||||
public void bindTo(MeterRegistry registry) {
|
||||
this.dataSources.stream(ObjectProvider.UNFILTERED).forEach((dataSource) -> {
|
||||
this.dataSources.stream(ObjectProvider.UNFILTERED, false).forEach((dataSource) -> {
|
||||
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class,
|
||||
HikariDataSource.class);
|
||||
if (hikariDataSource != null) {
|
||||
|
|
|
@ -19,12 +19,15 @@ package org.springframework.boot.actuate.autoconfigure.jdbc;
|
|||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration.RoutingDataSourceHealthContributor;
|
||||
|
@ -41,6 +44,7 @@ import org.springframework.boot.jdbc.DataSourceBuilder;
|
|||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -213,6 +217,16 @@ class DataSourceHealthContributorAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void prototypeDataSourceIsIgnored() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class, PrototypeDataSourceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(CompositeHealthContributor.class);
|
||||
assertThat(context.getBeansOfType(DataSourceHealthIndicator.class)).hasSize(1);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
static class DataSourceConfig {
|
||||
|
@ -317,4 +331,26 @@ class DataSourceHealthContributorAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class PrototypeDataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
|
||||
DataSource dataSourcePrototype(String username, String password) {
|
||||
return createHikariDataSource(username, password);
|
||||
}
|
||||
|
||||
private HikariDataSource createHikariDataSource(String username, String password) {
|
||||
String url = "jdbc:hsqldb:mem:test-" + UUID.randomUUID();
|
||||
HikariDataSource hikariDataSource = DataSourceBuilder.create()
|
||||
.url(url)
|
||||
.type(HikariDataSource.class)
|
||||
.username(username)
|
||||
.password(password)
|
||||
.build();
|
||||
return hikariDataSource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
|
||||
|
@ -39,6 +40,7 @@ import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
|
|||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.jdbc.datasource.DelegatingDataSource;
|
||||
|
@ -222,6 +224,19 @@ class DataSourcePoolMetricsAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void prototypeDataSourceIsIgnored() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(OneHikariDataSourceConfiguration.class, PrototypeDataSourceConfiguration.class)
|
||||
.run((context) -> {
|
||||
context.getBean("hikariDataSource", DataSource.class).getConnection();
|
||||
((DataSource) context.getBean("prototypeDataSource", "", "")).getConnection();
|
||||
MeterRegistry registry = context.getBean(MeterRegistry.class);
|
||||
assertThat(registry.get("hikaricp.connections").meter().getId().getTags())
|
||||
.containsExactly(Tag.of("pool", "hikariDataSource"));
|
||||
});
|
||||
}
|
||||
|
||||
private static HikariDataSource createHikariDataSource(String poolName) {
|
||||
String url = "jdbc:hsqldb:mem:test-" + UUID.randomUUID();
|
||||
HikariDataSource hikariDataSource = DataSourceBuilder.create().url(url).type(HikariDataSource.class).build();
|
||||
|
@ -334,6 +349,28 @@ class DataSourcePoolMetricsAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class PrototypeDataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
|
||||
DataSource prototypeDataSource(String username, String password) {
|
||||
return createHikariDataSource(username, password);
|
||||
}
|
||||
|
||||
private HikariDataSource createHikariDataSource(String username, String password) {
|
||||
String url = "jdbc:hsqldb:mem:test-" + UUID.randomUUID();
|
||||
HikariDataSource hikariDataSource = DataSourceBuilder.create()
|
||||
.url(url)
|
||||
.type(HikariDataSource.class)
|
||||
.username(username)
|
||||
.password(password)
|
||||
.build();
|
||||
return hikariDataSource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HikariSealingConfiguration {
|
||||
|
||||
|
|
Loading…
Reference in New Issue