Skip to content

Commit 9c6142f

Browse files
mp911deschauder
authored andcommitted
#95 - Use customized ConnectionFactory lookup to avoid AbstractR2dbcConfiguration proxying.
We now attempt to lookup ConnectionFactory from ApplicationContext and fall back to a local method call if ConnectionFactory is not exposed as bean. Original pull request: #96.
1 parent 6227d96 commit 9c6142f

File tree

2 files changed

+187
-6
lines changed

2 files changed

+187
-6
lines changed

src/main/java/org/springframework/data/r2dbc/config/AbstractR2dbcConfiguration.java

+40-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
import java.util.Collections;
2121
import java.util.Optional;
2222

23+
import org.springframework.beans.BeansException;
24+
import org.springframework.context.ApplicationContext;
25+
import org.springframework.context.ApplicationContextAware;
2326
import org.springframework.context.annotation.Bean;
2427
import org.springframework.context.annotation.Configuration;
2528
import org.springframework.core.convert.converter.Converter;
@@ -37,6 +40,7 @@
3740
import org.springframework.data.relational.core.conversion.BasicRelationalConverter;
3841
import org.springframework.data.relational.core.mapping.NamingStrategy;
3942
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
43+
import org.springframework.lang.Nullable;
4044
import org.springframework.util.Assert;
4145

4246
/**
@@ -48,8 +52,21 @@
4852
* @see DatabaseClient
4953
* @see org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories
5054
*/
51-
@Configuration
52-
public abstract class AbstractR2dbcConfiguration {
55+
@Configuration(proxyBeanMethods = false)
56+
public abstract class AbstractR2dbcConfiguration implements ApplicationContextAware {
57+
58+
private static final String CONNECTION_FACTORY_BEAN_NAME = "connectionFactory";
59+
60+
private @Nullable ApplicationContext context;
61+
62+
/*
63+
* (non-Javadoc)
64+
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
65+
*/
66+
@Override
67+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
68+
this.context = applicationContext;
69+
}
5370

5471
/**
5572
* Return a R2DBC {@link ConnectionFactory}. Annotate with {@link Bean} in case you want to expose a
@@ -91,7 +108,7 @@ public DatabaseClient databaseClient(ReactiveDataAccessStrategy dataAccessStrate
91108
Assert.notNull(exceptionTranslator, "ExceptionTranslator must not be null!");
92109

93110
return DatabaseClient.builder() //
94-
.connectionFactory(connectionFactory()) //
111+
.connectionFactory(lookupConnectionFactory()) //
95112
.dataAccessStrategy(dataAccessStrategy) //
96113
.exceptionTranslator(exceptionTranslator) //
97114
.build();
@@ -137,7 +154,7 @@ public ReactiveDataAccessStrategy reactiveDataAccessStrategy(RelationalMappingCo
137154

138155
MappingR2dbcConverter converter = new MappingR2dbcConverter(mappingContext, r2dbcCustomConversions);
139156

140-
return new DefaultReactiveDataAccessStrategy(getDialect(connectionFactory()), converter);
157+
return new DefaultReactiveDataAccessStrategy(getDialect(lookupConnectionFactory()), converter);
141158
}
142159

143160
/**
@@ -160,7 +177,7 @@ public R2dbcCustomConversions r2dbcCustomConversions() {
160177
*/
161178
protected StoreConversions getStoreConversions() {
162179

163-
Dialect dialect = getDialect(connectionFactory());
180+
Dialect dialect = getDialect(lookupConnectionFactory());
164181
return StoreConversions.of(dialect.getSimpleTypeHolder(), R2dbcCustomConversions.STORE_CONVERTERS);
165182
}
166183

@@ -172,6 +189,23 @@ protected StoreConversions getStoreConversions() {
172189
*/
173190
@Bean
174191
public R2dbcExceptionTranslator exceptionTranslator() {
175-
return new SqlErrorCodeR2dbcExceptionTranslator(connectionFactory());
192+
return new SqlErrorCodeR2dbcExceptionTranslator(lookupConnectionFactory());
193+
}
194+
195+
ConnectionFactory lookupConnectionFactory() {
196+
197+
ApplicationContext context = this.context;
198+
Assert.notNull(context, "ApplicationContext is not yet initialized");
199+
200+
String[] beanNamesForType = context.getBeanNamesForType(ConnectionFactory.class);
201+
202+
for (String beanName : beanNamesForType) {
203+
204+
if (beanName.equals(CONNECTION_FACTORY_BEAN_NAME)) {
205+
return context.getBean(CONNECTION_FACTORY_BEAN_NAME, ConnectionFactory.class);
206+
}
207+
}
208+
209+
return connectionFactory();
176210
}
177211
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.config;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
import static org.mockito.Mockito.*;
20+
21+
import io.r2dbc.h2.H2ConnectionConfiguration;
22+
import io.r2dbc.h2.H2ConnectionFactory;
23+
import io.r2dbc.spi.ConnectionFactory;
24+
25+
import org.junit.Test;
26+
27+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
28+
import org.springframework.context.annotation.Bean;
29+
import org.springframework.context.annotation.Configuration;
30+
import org.springframework.data.r2dbc.function.DatabaseClient;
31+
32+
/**
33+
* Tests for {@link AbstractR2dbcConfiguration}.
34+
*
35+
* @author Mark Paluch
36+
*/
37+
public class R2dbcConfigurationIntegrationTests {
38+
39+
@Test // gh-95
40+
public void shouldLookupConnectionFactoryThroughLocalCall() {
41+
42+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
43+
NonBeanConnectionFactoryConfiguration.class);
44+
45+
context.getBean(DatabaseClient.class);
46+
47+
NonBeanConnectionFactoryConfiguration bean = context.getBean(NonBeanConnectionFactoryConfiguration.class);
48+
49+
assertThat(context.getBeanNamesForType(ConnectionFactory.class)).isEmpty();
50+
assertThat(bean.callCounter).isGreaterThan(2);
51+
52+
context.stop();
53+
}
54+
55+
@Test // gh-95
56+
public void shouldLookupConnectionFactoryThroughLocalCallForExistingCustomBeans() {
57+
58+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
59+
CustomConnectionFactoryBeanNameConfiguration.class);
60+
61+
context.getBean(DatabaseClient.class);
62+
63+
CustomConnectionFactoryBeanNameConfiguration bean = context
64+
.getBean(CustomConnectionFactoryBeanNameConfiguration.class);
65+
66+
assertThat(context.getBeanNamesForType(ConnectionFactory.class)).hasSize(1).contains("myCustomBean");
67+
assertThat(bean.callCounter).isGreaterThan(2);
68+
69+
ConnectionFactoryWrapper wrapper = context.getBean(ConnectionFactoryWrapper.class);
70+
assertThat(wrapper.connectionFactory).isExactlyInstanceOf(H2ConnectionFactory.class);
71+
72+
context.stop();
73+
}
74+
75+
@Test // gh-95
76+
public void shouldRegisterConnectionFactory() {
77+
78+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
79+
BeanConnectionFactoryConfiguration.class);
80+
81+
context.getBean(DatabaseClient.class);
82+
83+
BeanConnectionFactoryConfiguration bean = context.getBean(BeanConnectionFactoryConfiguration.class);
84+
85+
assertThat(bean.callCounter).isEqualTo(1);
86+
assertThat(context.getBeanNamesForType(ConnectionFactory.class)).hasSize(1);
87+
88+
context.stop();
89+
}
90+
91+
@Configuration(proxyBeanMethods = false)
92+
static class NonBeanConnectionFactoryConfiguration extends AbstractR2dbcConfiguration {
93+
94+
int callCounter;
95+
96+
@Override
97+
public ConnectionFactory connectionFactory() {
98+
99+
callCounter++;
100+
return new H2ConnectionFactory(
101+
H2ConnectionConfiguration.builder().inMemory("foo").username("sa").password("").build());
102+
}
103+
}
104+
105+
@Configuration(proxyBeanMethods = false)
106+
static class CustomConnectionFactoryBeanNameConfiguration extends AbstractR2dbcConfiguration {
107+
108+
int callCounter;
109+
110+
@Bean
111+
public ConnectionFactory myCustomBean() {
112+
return mock(ConnectionFactory.class);
113+
}
114+
115+
@Override
116+
public ConnectionFactory connectionFactory() {
117+
118+
callCounter++;
119+
return new H2ConnectionFactory(
120+
H2ConnectionConfiguration.builder().inMemory("foo").username("sa").password("").build());
121+
}
122+
123+
@Bean
124+
ConnectionFactoryWrapper wrapper() {
125+
return new ConnectionFactoryWrapper(lookupConnectionFactory());
126+
}
127+
}
128+
129+
static class ConnectionFactoryWrapper {
130+
ConnectionFactory connectionFactory;
131+
132+
ConnectionFactoryWrapper(ConnectionFactory connectionFactory) {
133+
this.connectionFactory = connectionFactory;
134+
}
135+
}
136+
137+
@Configuration(proxyBeanMethods = false)
138+
static class BeanConnectionFactoryConfiguration extends NonBeanConnectionFactoryConfiguration {
139+
140+
@Override
141+
@Bean
142+
public ConnectionFactory connectionFactory() {
143+
return super.connectionFactory();
144+
}
145+
}
146+
147+
}

0 commit comments

Comments
 (0)