Skip to content

Commit d549e60

Browse files
christophstroblsnicoll
authored andcommitted
Add support for com.mongodb.client.MongoClient
Next to com.mongodb.MongoClient the MongoDB Java driver offers the com.mongodb.client.MongoClient as entry point for database and collection operations. Spring Data MongoDB supports c.m.client.MongoClient via its MongoDbFactory using SimpleMongoClientDbFactory. The MongoAutoConfiguration now backs off if any of those two clients is already defined in the Application context allowing MongoDataAutoConfiguration to pick up the users driver implementation of choice. See gh-14176
1 parent 19d17e7 commit d549e60

File tree

4 files changed

+155
-6
lines changed

4 files changed

+155
-6
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java

Lines changed: 107 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,35 @@
1616

1717
package org.springframework.boot.autoconfigure.data.mongo;
1818

19+
import java.util.Arrays;
20+
import java.util.List;
21+
1922
import com.mongodb.ClientSessionOptions;
2023
import com.mongodb.DB;
2124
import com.mongodb.MongoClient;
2225
import com.mongodb.client.ClientSession;
2326
import com.mongodb.client.MongoDatabase;
2427

28+
import org.springframework.beans.factory.ObjectProvider;
2529
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2630
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
31+
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
2732
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2833
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2934
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
35+
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration.AnySyncMongoClientAvailable;
3036
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
3137
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
3238
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3339
import org.springframework.context.annotation.Bean;
40+
import org.springframework.context.annotation.Conditional;
3441
import org.springframework.context.annotation.Configuration;
3542
import org.springframework.context.annotation.Import;
3643
import org.springframework.dao.DataAccessException;
3744
import org.springframework.dao.support.PersistenceExceptionTranslator;
3845
import org.springframework.data.mongodb.MongoDbFactory;
3946
import org.springframework.data.mongodb.core.MongoTemplate;
47+
import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory;
4048
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
4149
import org.springframework.data.mongodb.core.convert.DbRefResolver;
4250
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
@@ -63,27 +71,35 @@
6371
* @author Phillip Webb
6472
* @author Eddú Meléndez
6573
* @author Stephane Nicoll
74+
* @author Christoph Strobl
6675
* @since 1.1.0
6776
*/
6877
@Configuration
6978
@ConditionalOnClass({ MongoClient.class, MongoTemplate.class })
70-
@ConditionalOnBean(MongoClient.class)
79+
@Conditional(AnySyncMongoClientAvailable.class)
7180
@EnableConfigurationProperties(MongoProperties.class)
7281
@Import(MongoDataConfiguration.class)
7382
@AutoConfigureAfter(MongoAutoConfiguration.class)
7483
public class MongoDataAutoConfiguration {
7584

7685
private final MongoProperties properties;
7786

78-
public MongoDataAutoConfiguration(MongoProperties properties) {
87+
private final MongoDbFactoryFactory dbFactoryFactory;
88+
89+
public MongoDataAutoConfiguration(ObjectProvider<MongoClient> mongoClientProvider,
90+
ObjectProvider<com.mongodb.client.MongoClient> mongoClientClientProvider,
91+
MongoProperties properties) {
92+
7993
this.properties = properties;
94+
this.dbFactoryFactory = new MongoDbFactoryFactory(mongoClientProvider,
95+
mongoClientClientProvider);
8096
}
8197

8298
@Bean
99+
@Conditional(AnySyncMongoClientAvailable.class)
83100
@ConditionalOnMissingBean(MongoDbFactory.class)
84-
public SimpleMongoDbFactory mongoDbFactory(MongoClient mongo) {
85-
String database = this.properties.getMongoClientDatabase();
86-
return new SimpleMongoDbFactory(mongo, database);
101+
public MongoDbFactory mongoDbFactory() {
102+
return this.dbFactoryFactory.getFor(this.properties.getMongoClientDatabase());
87103
}
88104

89105
@Bean
@@ -166,4 +182,90 @@ public MongoDbFactory withSession(ClientSession session) {
166182

167183
}
168184

185+
/**
186+
* Check if either {@link com.mongodb.MongoClient} or
187+
* {@link com.mongodb.client.MongoClient} is already defined in the
188+
* {@link org.springframework.context.ApplicationContext}.
189+
*
190+
* @author Christoph Strobl
191+
* @since 2.1
192+
*/
193+
static class AnySyncMongoClientAvailable extends AnyNestedCondition {
194+
195+
AnySyncMongoClientAvailable() {
196+
super(ConfigurationPhase.REGISTER_BEAN);
197+
}
198+
199+
@ConditionalOnBean(com.mongodb.MongoClient.class)
200+
static class MongoClientPreferred {
201+
202+
}
203+
204+
@ConditionalOnBean(com.mongodb.client.MongoClient.class)
205+
static class MongoClientClientPreferred {
206+
207+
}
208+
209+
}
210+
211+
/**
212+
* Encapsulation of {@link MongoDbFactory} creation depending on available beans
213+
* {@link com.mongodb.MongoClient} or {@link com.mongodb.client.MongoClient} expressed
214+
* via the given {@link ObjectProvider ObjectProviders}. Prefers the first available
215+
* MongoDB client creating a suitable instance of {@link MongoDbFactory} for it.
216+
*
217+
* @author Christoph Strobl
218+
* @since 2.1
219+
*/
220+
static class MongoDbFactoryFactory {
221+
222+
private final List<ObjectProvider<?>> clientProviders;
223+
224+
/**
225+
* Create new instance of {@link MongoDbFactoryFactory}.
226+
* @param clientProviders order matters here, as we choose the first available
227+
* one.
228+
*/
229+
MongoDbFactoryFactory(ObjectProvider<?>... clientProviders) {
230+
this.clientProviders = Arrays.asList(clientProviders);
231+
}
232+
233+
/**
234+
* Get the {@link MongoDbFactory} suitable for the first available MongoDB client.
235+
* @param database the name of the default database to return on
236+
* {@link MongoDbFactory#getDb()}.
237+
* @return new instance of {@link MongoDbFactory} suitable for the first available
238+
* MongoDB client.
239+
*/
240+
MongoDbFactory getFor(String database) {
241+
242+
Object client = findAvailableClientProvider();
243+
244+
if (client instanceof MongoClient) {
245+
return new SimpleMongoDbFactory(MongoClient.class.cast(client), database);
246+
}
247+
248+
if (client instanceof com.mongodb.client.MongoClient) {
249+
return new SimpleMongoClientDbFactory(
250+
com.mongodb.client.MongoClient.class.cast(client), database);
251+
}
252+
253+
return null;
254+
}
255+
256+
private Object findAvailableClientProvider() {
257+
258+
for (ObjectProvider<?> provider : this.clientProviders) {
259+
Object client = provider.getIfAvailable();
260+
if (client != null) {
261+
return client;
262+
}
263+
}
264+
265+
throw new IllegalStateException(
266+
"Expected to find at least one MongoDB client.");
267+
}
268+
269+
}
270+
169271
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ public void close() {
6565
}
6666

6767
@Bean
68-
@ConditionalOnMissingBean
68+
@ConditionalOnMissingBean(type = { "com.mongodb.MongoClient",
69+
"com.mongodb.client.MongoClient" })
6970
public MongoClient mongo() {
7071
this.mongo = this.factory.createMongoClient(this.options);
7172
return this.mongo;

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfigurationTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Set;
2222

2323
import com.mongodb.MongoClient;
24+
import com.mongodb.client.MongoClients;
2425
import org.junit.Test;
2526

2627
import org.springframework.beans.factory.BeanCreationException;
@@ -39,7 +40,9 @@
3940
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
4041
import org.springframework.data.mapping.model.FieldNamingStrategy;
4142
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
43+
import org.springframework.data.mongodb.MongoDbFactory;
4244
import org.springframework.data.mongodb.core.MongoTemplate;
45+
import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory;
4346
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
4447
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
4548
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
@@ -173,6 +176,16 @@ public void backsOffIfMongoClientBeanIsNotPresent() {
173176
.doesNotHaveBean(MongoDataAutoConfiguration.class));
174177
}
175178

179+
@Test
180+
public void createsMongoDbFactoryForMongoClientClientWhenBeanPresent() {
181+
182+
this.contextRunner.withUserConfiguration(WithMongoClientClientConfiguration.class)
183+
.run((context) -> {
184+
MongoDbFactory dbFactory = context.getBean(MongoDbFactory.class);
185+
assertThat(dbFactory).isInstanceOf(SimpleMongoClientDbFactory.class);
186+
});
187+
}
188+
176189
@SuppressWarnings({ "unchecked", "rawtypes" })
177190
private static void assertDomainTypesDiscovered(MongoMappingContext mappingContext,
178191
Class<?>... types) {
@@ -197,6 +210,16 @@ static class EntityScanConfig {
197210

198211
}
199212

213+
@Configuration
214+
static class WithMongoClientClientConfiguration {
215+
216+
@Bean
217+
com.mongodb.client.MongoClient mongoClient() {
218+
return MongoClients.create();
219+
}
220+
221+
}
222+
200223
private static class MyConverter implements Converter<MongoClient, Boolean> {
201224

202225
@Override

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@
2020

2121
import com.mongodb.MongoClient;
2222
import com.mongodb.MongoClientOptions;
23+
import com.mongodb.client.MongoClients;
2324
import org.junit.Test;
2425

26+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2527
import org.springframework.boot.autoconfigure.AutoConfigurations;
2628
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2729
import org.springframework.context.annotation.Bean;
2830
import org.springframework.context.annotation.Configuration;
2931

3032
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
3134
import static org.mockito.Mockito.mock;
3235

3336
/**
@@ -78,6 +81,17 @@ public void optionsSslConfig() {
7881
});
7982
}
8083

84+
@Test
85+
public void doesNotCreateMongoClientWhenAlreadyDefined() {
86+
87+
this.contextRunner
88+
.withPropertyValues("spring.data.mongodb.uri:mongodb://localhost/test")
89+
.withUserConfiguration(ConfigurationWithClientMongoClient.class)
90+
.run((context) -> assertThatExceptionOfType(
91+
NoSuchBeanDefinitionException.class)
92+
.isThrownBy(() -> context.getBean(MongoClient.class)));
93+
}
94+
8195
@Configuration
8296
static class OptionsConfig {
8397

@@ -104,4 +118,13 @@ public SocketFactory mySocketFactory() {
104118

105119
}
106120

121+
static class ConfigurationWithClientMongoClient {
122+
123+
@Bean
124+
com.mongodb.client.MongoClient mongoClient() {
125+
return MongoClients.create();
126+
}
127+
128+
}
129+
107130
}

0 commit comments

Comments
 (0)