Skip to content

Commit 627ac04

Browse files
committed
Polish "Handle lazy connection proxy datasource in DataSourceBuilder"
See gh-50271
1 parent 460318e commit 627ac04

3 files changed

Lines changed: 78 additions & 22 deletions

File tree

module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceBuilder.java

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141

4242
import org.springframework.beans.BeanUtils;
4343
import org.springframework.core.ResolvableType;
44-
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
4544
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
4645
import org.springframework.lang.Contract;
4746
import org.springframework.util.Assert;
@@ -238,27 +237,7 @@ public static DataSourceBuilder<?> create(@Nullable ClassLoader classLoader) {
238237
* @return a new {@link DataSource} builder
239238
*/
240239
public static DataSourceBuilder<?> derivedFrom(DataSource dataSource) {
241-
return new DataSourceBuilder<>(unwrap(dataSource));
242-
}
243-
244-
private static DataSource unwrap(DataSource dataSource) {
245-
if ((dataSource instanceof LazyConnectionDataSourceProxy dataSourceProxy)
246-
&& (dataSourceProxy.getTargetDataSource() != null)) {
247-
dataSource = dataSourceProxy.getTargetDataSource();
248-
}
249-
try {
250-
while (dataSource.isWrapperFor(DataSource.class)) {
251-
DataSource unwrapped = dataSource.unwrap(DataSource.class);
252-
if (unwrapped == dataSource) {
253-
return unwrapped;
254-
}
255-
dataSource = unwrapped;
256-
}
257-
}
258-
catch (SQLException ex) {
259-
// Try to continue with the existing, potentially still wrapped, DataSource
260-
}
261-
return dataSource;
240+
return new DataSourceBuilder<>(DataSourceUnwrapper.unwrapRoot(dataSource));
262241
}
263242

264243
/**

module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,37 @@ private DataSourceUnwrapper() {
9393
return unwrap(dataSource, target, target);
9494
}
9595

96+
/**
97+
* Return the root {@link DataSource} by recursively unwrapping all
98+
* {@link org.springframework.jdbc.datasource.DelegatingDataSource delegating}, proxy,
99+
* and {@link java.sql.Wrapper} layers until no further unwrapping is possible.
100+
* @param dataSource the datasource to unwrap
101+
* @return the root {@link DataSource}
102+
* @since 4.1.0
103+
*/
104+
public static DataSource unwrapRoot(DataSource dataSource) {
105+
if (DELEGATING_DATA_SOURCE_PRESENT) {
106+
DataSource targetDataSource = DelegatingDataSourceUnwrapper.getTargetDataSource(dataSource);
107+
if (targetDataSource != null) {
108+
return unwrapRoot(targetDataSource);
109+
}
110+
}
111+
if (AopUtils.isAopProxy(dataSource)) {
112+
Object proxyTarget = AopProxyUtils.getSingletonTarget(dataSource);
113+
if (proxyTarget instanceof DataSource proxyDataSource) {
114+
return unwrapRoot(proxyDataSource);
115+
}
116+
}
117+
DataSource unwrapped = safeUnwrap(dataSource);
118+
if (unwrapped != null) {
119+
if (unwrapped == dataSource) {
120+
return unwrapped;
121+
}
122+
return unwrapRoot(unwrapped);
123+
}
124+
return dataSource;
125+
}
126+
96127
private static <S> @Nullable S safeUnwrap(Wrapper wrapper, Class<S> target) {
97128
try {
98129
if (target.isInterface() && wrapper.isWrapperFor(target)) {
@@ -105,6 +136,18 @@ private DataSourceUnwrapper() {
105136
return null;
106137
}
107138

139+
private static @Nullable DataSource safeUnwrap(DataSource dataSource) {
140+
try {
141+
if (dataSource.isWrapperFor(DataSource.class)) {
142+
return dataSource.unwrap(DataSource.class);
143+
}
144+
}
145+
catch (Exception ex) {
146+
// continue
147+
}
148+
return null;
149+
}
150+
108151
private static final class DelegatingDataSourceUnwrapper {
109152

110153
private static @Nullable DataSource getTargetDataSource(DataSource dataSource) {

module/spring-boot-jdbc/src/test/java/org/springframework/boot/jdbc/DataSourceUnwrapperTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,40 @@ void unwrapDataSourceProxy() {
106106
.isSameAs(dataSource);
107107
}
108108

109+
@Test
110+
void unwrapRootWithPlainDataSource() {
111+
DataSource dataSource = new HikariDataSource();
112+
assertThat(DataSourceUnwrapper.unwrapRoot(dataSource)).isSameAs(dataSource);
113+
}
114+
115+
@Test
116+
void unwrapRootWithDelegate() {
117+
DataSource dataSource = new HikariDataSource();
118+
DataSource actual = wrapInDelegate(wrapInDelegate(dataSource));
119+
assertThat(DataSourceUnwrapper.unwrapRoot(actual)).isSameAs(dataSource);
120+
}
121+
122+
@Test
123+
void unwrapRootWithProxy() {
124+
DataSource dataSource = new HikariDataSource();
125+
DataSource actual = wrapInProxy(wrapInProxy(dataSource));
126+
assertThat(DataSourceUnwrapper.unwrapRoot(actual)).isSameAs(dataSource);
127+
}
128+
129+
@Test
130+
void unwrapRootWithLazyConnectionDataSource() {
131+
DataSource dataSource = new HikariDataSource();
132+
DataSource actual = new LazyConnectionDataSourceProxy(dataSource);
133+
assertThat(DataSourceUnwrapper.unwrapRoot(actual)).isSameAs(dataSource);
134+
}
135+
136+
@Test
137+
void unwrapRootWithSeveralLevelOfWrapping() {
138+
DataSource dataSource = new HikariDataSource();
139+
DataSource actual = wrapInProxy(wrapInDelegate(wrapInDelegate(wrapInProxy(wrapInDelegate(dataSource)))));
140+
assertThat(DataSourceUnwrapper.unwrapRoot(actual)).isSameAs(dataSource);
141+
}
142+
109143
@Test
110144
void unwrappingIsNotAttemptedWhenTargetIsNotAnInterface() {
111145
DataSource dataSource = mock(DataSource.class);

0 commit comments

Comments
 (0)