Skip to content

Commit 0f652cd

Browse files
committed
Fix injection point with proxied DataSource
Prior to this commit, the `ApplicationContext` couldn't start with a JDK-proxied `HikariDataSource` as the JMX auto-configuration was attempting to inject a (too narrowed) `HikariDataSource`. This commit rather injects a regular `DataSource` and attempt to unwrap it as a `HikariDataSource`. Closes gh-12271
1 parent 5fa71a1 commit 0f652cd

File tree

2 files changed

+66
-7
lines changed

2 files changed

+66
-7
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceJmxConfiguration.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -48,23 +48,33 @@ class DataSourceJmxConfiguration {
4848

4949
@Configuration
5050
@ConditionalOnClass(HikariDataSource.class)
51-
@ConditionalOnSingleCandidate(HikariDataSource.class)
51+
@ConditionalOnSingleCandidate(DataSource.class)
5252
static class Hikari {
5353

54-
private final HikariDataSource dataSource;
54+
private final DataSource dataSource;
5555

5656
private final ObjectProvider<MBeanExporter> mBeanExporter;
5757

58-
Hikari(HikariDataSource dataSource, ObjectProvider<MBeanExporter> mBeanExporter) {
58+
Hikari(DataSource dataSource, ObjectProvider<MBeanExporter> mBeanExporter) {
5959
this.dataSource = dataSource;
6060
this.mBeanExporter = mBeanExporter;
6161
}
6262

6363
@PostConstruct
6464
public void validateMBeans() {
65-
MBeanExporter exporter = this.mBeanExporter.getIfUnique();
66-
if (exporter != null && this.dataSource.isRegisterMbeans()) {
67-
exporter.addExcludedBean("dataSource");
65+
HikariDataSource hikariDataSource = unwrapHikariDataSource();
66+
if (hikariDataSource != null && hikariDataSource.isRegisterMbeans()) {
67+
this.mBeanExporter.ifUnique((exporter) ->
68+
exporter.addExcludedBean("dataSource"));
69+
}
70+
}
71+
72+
private HikariDataSource unwrapHikariDataSource() {
73+
try {
74+
return this.dataSource.unwrap(HikariDataSource.class);
75+
}
76+
catch (SQLException ex) {
77+
return null;
6878
}
6979
}
7080

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceJmxConfigurationTests.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@
3131
import org.apache.tomcat.jdbc.pool.jmx.ConnectionPool;
3232
import org.junit.Test;
3333

34+
import org.springframework.aop.framework.ProxyFactory;
35+
import org.springframework.beans.factory.config.BeanPostProcessor;
3436
import org.springframework.boot.autoconfigure.AutoConfigurations;
3537
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
3638
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
39+
import org.springframework.context.annotation.Bean;
40+
import org.springframework.context.annotation.Configuration;
3741

3842
import static org.assertj.core.api.Assertions.assertThat;
3943

@@ -101,6 +105,24 @@ public void hikariAutoConfiguredUsesJmsFlag() {
101105
});
102106
}
103107

108+
@Test
109+
public void hikariProxiedCanUseRegisterMBeans() {
110+
String poolName = UUID.randomUUID().toString();
111+
this.contextRunner.withUserConfiguration(DataSourceProxyConfiguration.class)
112+
.withPropertyValues(
113+
"spring.datasource.type=" + HikariDataSource.class.getName(),
114+
"spring.datasource.name=" + poolName,
115+
"spring.datasource.hikari.register-mbeans=true")
116+
.run((context) -> {
117+
assertThat(context).hasSingleBean(javax.sql.DataSource.class);
118+
HikariDataSource hikariDataSource = context.getBean(
119+
javax.sql.DataSource.class).unwrap(HikariDataSource.class);
120+
assertThat(hikariDataSource.isRegisterMbeans()).isTrue();
121+
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
122+
validateHikariMBeansRegistration(mBeanServer, poolName, true);
123+
});
124+
}
125+
104126
private void validateHikariMBeansRegistration(MBeanServer mBeanServer,
105127
String poolName, boolean expected) throws MalformedObjectNameException {
106128
assertThat(mBeanServer.isRegistered(
@@ -130,4 +152,31 @@ public void tomcatAutoConfiguredCanExposeMBeanPool() {
130152
});
131153
}
132154

155+
@Configuration
156+
static class DataSourceProxyConfiguration {
157+
158+
@Bean
159+
public static DataSourceBeanPostProcessor dataSourceBeanPostProcessor() {
160+
return new DataSourceBeanPostProcessor();
161+
}
162+
163+
}
164+
165+
166+
private static class DataSourceBeanPostProcessor implements BeanPostProcessor {
167+
168+
@Override
169+
public Object postProcessAfterInitialization(Object bean, String beanName) {
170+
if (bean instanceof javax.sql.DataSource) {
171+
return wrap((javax.sql.DataSource) bean);
172+
}
173+
return bean;
174+
}
175+
176+
private static javax.sql.DataSource wrap(javax.sql.DataSource dataSource) {
177+
return (javax.sql.DataSource) new ProxyFactory(dataSource).getProxy();
178+
}
179+
180+
}
181+
133182
}

0 commit comments

Comments
 (0)