mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Unwrap Datasource against an actual interface
This commit updates DataSourceUnwrapper to take a separate interface type argument if the target datasource has to be unwrapped, given that the target type is usually not an interface. Closes gh-24697
This commit is contained in:
parent
51636eef97
commit
283ed48d63
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -24,6 +24,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfigMXBean;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import com.zaxxer.hikari.metrics.micrometer.MicrometerMetricsTrackerFactory;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
@ -112,7 +113,8 @@ public class DataSourcePoolMetricsAutoConfiguration {
|
||||
@Autowired
|
||||
void bindMetricsRegistryToHikariDataSources(Collection<DataSource> dataSources) {
|
||||
for (DataSource dataSource : dataSources) {
|
||||
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariDataSource.class);
|
||||
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class,
|
||||
HikariDataSource.class);
|
||||
if (hikariDataSource != null) {
|
||||
bindMetricsRegistryToHikariDataSource(hikariDataSource);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -20,10 +20,12 @@ import java.sql.SQLException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfigMXBean;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
|
||||
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@ -62,7 +64,8 @@ class DataSourceJmxConfiguration {
|
||||
}
|
||||
|
||||
private void validateMBeans() {
|
||||
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(this.dataSource, HikariDataSource.class);
|
||||
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(this.dataSource, HikariConfigMXBean.class,
|
||||
HikariDataSource.class);
|
||||
if (hikariDataSource != null && hikariDataSource.isRegisterMbeans()) {
|
||||
this.mBeanExporter.ifUnique((exporter) -> exporter.addExcludedBean("dataSource"));
|
||||
}
|
||||
@ -79,7 +82,8 @@ class DataSourceJmxConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "dataSourceMBean")
|
||||
Object dataSourceMBean(DataSource dataSource) {
|
||||
DataSourceProxy dataSourceProxy = DataSourceUnwrapper.unwrap(dataSource, DataSourceProxy.class);
|
||||
DataSourceProxy dataSourceProxy = DataSourceUnwrapper.unwrap(dataSource, PoolConfiguration.class,
|
||||
DataSourceProxy.class);
|
||||
if (dataSourceProxy != null) {
|
||||
try {
|
||||
return dataSourceProxy.createPool().getJmxPool();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,8 +16,11 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc.metadata;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfigMXBean;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.apache.commons.dbcp2.BasicDataSourceMXBean;
|
||||
import org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.jdbc.DataSourceUnwrapper;
|
||||
@ -46,7 +49,7 @@ public class DataSourcePoolMetadataProvidersConfiguration {
|
||||
DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() {
|
||||
return (dataSource) -> {
|
||||
org.apache.tomcat.jdbc.pool.DataSource tomcatDataSource = DataSourceUnwrapper.unwrap(dataSource,
|
||||
org.apache.tomcat.jdbc.pool.DataSource.class);
|
||||
ConnectionPoolMBean.class, org.apache.tomcat.jdbc.pool.DataSource.class);
|
||||
if (tomcatDataSource != null) {
|
||||
return new TomcatDataSourcePoolMetadata(tomcatDataSource);
|
||||
}
|
||||
@ -63,7 +66,8 @@ public class DataSourcePoolMetadataProvidersConfiguration {
|
||||
@Bean
|
||||
DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() {
|
||||
return (dataSource) -> {
|
||||
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariDataSource.class);
|
||||
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class,
|
||||
HikariDataSource.class);
|
||||
if (hikariDataSource != null) {
|
||||
return new HikariDataSourcePoolMetadata(hikariDataSource);
|
||||
}
|
||||
@ -80,7 +84,8 @@ public class DataSourcePoolMetadataProvidersConfiguration {
|
||||
@Bean
|
||||
DataSourcePoolMetadataProvider commonsDbcp2PoolDataSourceMetadataProvider() {
|
||||
return (dataSource) -> {
|
||||
BasicDataSource dbcpDataSource = DataSourceUnwrapper.unwrap(dataSource, BasicDataSource.class);
|
||||
BasicDataSource dbcpDataSource = DataSourceUnwrapper.unwrap(dataSource, BasicDataSourceMXBean.class,
|
||||
BasicDataSource.class);
|
||||
if (dbcpDataSource != null) {
|
||||
return new CommonsDbcp2DataSourcePoolMetadata(dbcpDataSource);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -43,33 +43,50 @@ public final class DataSourceUnwrapper {
|
||||
|
||||
/**
|
||||
* Return an object that implements the given {@code target} type, unwrapping delegate
|
||||
* or proxy if necessary.
|
||||
* or proxy if necessary using the specified {@code unwrapInterface}.
|
||||
* @param dataSource the datasource to handle
|
||||
* @param unwrapInterface the interface that the target type must implement
|
||||
* @param target the type that the result must implement
|
||||
* @param <I> the interface that the target type must implement
|
||||
* @param <T> the target type
|
||||
* @return an object that implements the target type or {@code null}
|
||||
* @see Wrapper#unwrap(Class)
|
||||
*/
|
||||
public static <I, T extends I> T unwrap(DataSource dataSource, Class<I> unwrapInterface, Class<T> target) {
|
||||
if (target.isInstance(dataSource)) {
|
||||
return target.cast(dataSource);
|
||||
}
|
||||
I unwrapped = safeUnwrap(dataSource, unwrapInterface);
|
||||
if (unwrapped != null && unwrapInterface.isAssignableFrom(target)) {
|
||||
return target.cast(unwrapped);
|
||||
}
|
||||
if (DELEGATING_DATA_SOURCE_PRESENT) {
|
||||
DataSource targetDataSource = DelegatingDataSourceUnwrapper.getTargetDataSource(dataSource);
|
||||
if (targetDataSource != null) {
|
||||
return unwrap(targetDataSource, unwrapInterface, target);
|
||||
}
|
||||
}
|
||||
if (AopUtils.isAopProxy(dataSource)) {
|
||||
Object proxyTarget = AopProxyUtils.getSingletonTarget(dataSource);
|
||||
if (proxyTarget instanceof DataSource) {
|
||||
return unwrap((DataSource) proxyTarget, unwrapInterface, target);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an object that implements the given {@code target} type, unwrapping delegate
|
||||
* or proxy if necessary. Consider using {@link #unwrap(DataSource, Class, Class)} as
|
||||
* {@link Wrapper#unwrap(Class) unwrapping} won't be considered if {@code target} is
|
||||
* not an interface.
|
||||
* @param dataSource the datasource to handle
|
||||
* @param target the type that the result must implement
|
||||
* @param <T> the target type
|
||||
* @return an object that implements the target type or {@code null}
|
||||
*/
|
||||
public static <T> T unwrap(DataSource dataSource, Class<T> target) {
|
||||
if (target.isInstance(dataSource)) {
|
||||
return target.cast(dataSource);
|
||||
}
|
||||
T unwrapped = safeUnwrap(dataSource, target);
|
||||
if (unwrapped != null) {
|
||||
return unwrapped;
|
||||
}
|
||||
if (DELEGATING_DATA_SOURCE_PRESENT) {
|
||||
DataSource targetDataSource = DelegatingDataSourceUnwrapper.getTargetDataSource(dataSource);
|
||||
if (targetDataSource != null) {
|
||||
return unwrap(targetDataSource, target);
|
||||
}
|
||||
}
|
||||
if (AopUtils.isAopProxy(dataSource)) {
|
||||
Object proxyTarget = AopProxyUtils.getSingletonTarget(dataSource);
|
||||
if (proxyTarget instanceof DataSource) {
|
||||
return unwrap((DataSource) proxyTarget, target);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return unwrap(dataSource, target, target);
|
||||
}
|
||||
|
||||
private static <S> S safeUnwrap(Wrapper wrapper, Class<S> target) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -18,8 +18,10 @@ package org.springframework.boot.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfigMXBean;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
|
||||
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
@ -39,14 +41,16 @@ class DataSourceUnwrapperNoSpringJdbcTests {
|
||||
void unwrapWithProxy() {
|
||||
DataSource dataSource = new HikariDataSource();
|
||||
DataSource actual = wrapInProxy(wrapInProxy(dataSource));
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, HikariDataSource.class)).isSameAs(dataSource);
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, HikariConfigMXBean.class, HikariDataSource.class))
|
||||
.isSameAs(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapDataSourceProxy() {
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
|
||||
DataSource actual = wrapInProxy(wrapInProxy(dataSource));
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, DataSourceProxy.class)).isSameAs(dataSource);
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, PoolConfiguration.class, DataSourceProxy.class))
|
||||
.isSameAs(dataSource);
|
||||
}
|
||||
|
||||
private DataSource wrapInProxy(DataSource dataSource) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -21,13 +21,16 @@ import java.util.function.Consumer;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfigMXBean;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
|
||||
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.jdbc.datasource.DelegatingDataSource;
|
||||
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
|
||||
import org.springframework.jdbc.datasource.SmartDataSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@ -44,52 +47,59 @@ class DataSourceUnwrapperTests {
|
||||
@Test
|
||||
void unwrapWithTarget() {
|
||||
DataSource dataSource = new HikariDataSource();
|
||||
assertThat(DataSourceUnwrapper.unwrap(dataSource, HikariDataSource.class)).isSameAs(dataSource);
|
||||
assertThat(DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class, HikariDataSource.class))
|
||||
.isSameAs(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithWrongTarget() {
|
||||
DataSource dataSource = new HikariDataSource();
|
||||
assertThat(DataSourceUnwrapper.unwrap(dataSource, SingleConnectionDataSource.class)).isNull();
|
||||
assertThat(DataSourceUnwrapper.unwrap(dataSource, SmartDataSource.class, SingleConnectionDataSource.class))
|
||||
.isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithDelegate() {
|
||||
DataSource dataSource = new HikariDataSource();
|
||||
DataSource actual = wrapInDelegate(wrapInDelegate(dataSource));
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, HikariDataSource.class)).isSameAs(dataSource);
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, HikariConfigMXBean.class, HikariDataSource.class))
|
||||
.isSameAs(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithProxy() {
|
||||
DataSource dataSource = new HikariDataSource();
|
||||
DataSource actual = wrapInProxy(wrapInProxy(dataSource));
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, HikariDataSource.class)).isSameAs(dataSource);
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, HikariConfigMXBean.class, HikariDataSource.class))
|
||||
.isSameAs(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithProxyAndDelegate() {
|
||||
DataSource dataSource = new HikariDataSource();
|
||||
DataSource actual = wrapInProxy(wrapInDelegate(dataSource));
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, HikariDataSource.class)).isSameAs(dataSource);
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, HikariConfigMXBean.class, HikariDataSource.class))
|
||||
.isSameAs(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithSeveralLevelOfWrapping() {
|
||||
DataSource dataSource = new HikariDataSource();
|
||||
DataSource actual = wrapInProxy(wrapInDelegate(wrapInDelegate(wrapInProxy(wrapInDelegate(dataSource)))));
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, HikariDataSource.class)).isSameAs(dataSource);
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, HikariConfigMXBean.class, HikariDataSource.class))
|
||||
.isSameAs(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapDataSourceProxy() {
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
|
||||
DataSource actual = wrapInDelegate(wrapInProxy(dataSource));
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, DataSourceProxy.class)).isSameAs(dataSource);
|
||||
assertThat(DataSourceUnwrapper.unwrap(actual, PoolConfiguration.class, DataSourceProxy.class))
|
||||
.isSameAs(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrappingIsNotAttemptedWhenTargetIsNotAnInterface() throws SQLException {
|
||||
void unwrappingIsNotAttemptedWhenTargetIsNotAnInterface() {
|
||||
DataSource dataSource = mock(DataSource.class);
|
||||
assertThat(DataSourceUnwrapper.unwrap(dataSource, HikariDataSource.class)).isNull();
|
||||
verifyNoMoreInteractions(dataSource);
|
||||
|
Loading…
Reference in New Issue
Block a user