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
pull/14215/merge
Christoph Strobl 6 years ago committed by Stephane Nicoll
parent 19d17e7ec9
commit d549e6001a

@ -16,27 +16,35 @@
package org.springframework.boot.autoconfigure.data.mongo;
import java.util.Arrays;
import java.util.List;
import com.mongodb.ClientSessionOptions;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoDatabase;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration.AnySyncMongoClientAvailable;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
@ -63,11 +71,12 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Christoph Strobl
* @since 1.1.0
*/
@Configuration
@ConditionalOnClass({ MongoClient.class, MongoTemplate.class })
@ConditionalOnBean(MongoClient.class)
@Conditional(AnySyncMongoClientAvailable.class)
@EnableConfigurationProperties(MongoProperties.class)
@Import(MongoDataConfiguration.class)
@AutoConfigureAfter(MongoAutoConfiguration.class)
@ -75,15 +84,22 @@ public class MongoDataAutoConfiguration {
private final MongoProperties properties;
public MongoDataAutoConfiguration(MongoProperties properties) {
private final MongoDbFactoryFactory dbFactoryFactory;
public MongoDataAutoConfiguration(ObjectProvider<MongoClient> mongoClientProvider,
ObjectProvider<com.mongodb.client.MongoClient> mongoClientClientProvider,
MongoProperties properties) {
this.properties = properties;
this.dbFactoryFactory = new MongoDbFactoryFactory(mongoClientProvider,
mongoClientClientProvider);
}
@Bean
@Conditional(AnySyncMongoClientAvailable.class)
@ConditionalOnMissingBean(MongoDbFactory.class)
public SimpleMongoDbFactory mongoDbFactory(MongoClient mongo) {
String database = this.properties.getMongoClientDatabase();
return new SimpleMongoDbFactory(mongo, database);
public MongoDbFactory mongoDbFactory() {
return this.dbFactoryFactory.getFor(this.properties.getMongoClientDatabase());
}
@Bean
@ -166,4 +182,90 @@ public class MongoDataAutoConfiguration {
}
/**
* Check if either {@link com.mongodb.MongoClient} or
* {@link com.mongodb.client.MongoClient} is already defined in the
* {@link org.springframework.context.ApplicationContext}.
*
* @author Christoph Strobl
* @since 2.1
*/
static class AnySyncMongoClientAvailable extends AnyNestedCondition {
AnySyncMongoClientAvailable() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnBean(com.mongodb.MongoClient.class)
static class MongoClientPreferred {
}
@ConditionalOnBean(com.mongodb.client.MongoClient.class)
static class MongoClientClientPreferred {
}
}
/**
* Encapsulation of {@link MongoDbFactory} creation depending on available beans
* {@link com.mongodb.MongoClient} or {@link com.mongodb.client.MongoClient} expressed
* via the given {@link ObjectProvider ObjectProviders}. Prefers the first available
* MongoDB client creating a suitable instance of {@link MongoDbFactory} for it.
*
* @author Christoph Strobl
* @since 2.1
*/
static class MongoDbFactoryFactory {
private final List<ObjectProvider<?>> clientProviders;
/**
* Create new instance of {@link MongoDbFactoryFactory}.
* @param clientProviders order matters here, as we choose the first available
* one.
*/
MongoDbFactoryFactory(ObjectProvider<?>... clientProviders) {
this.clientProviders = Arrays.asList(clientProviders);
}
/**
* Get the {@link MongoDbFactory} suitable for the first available MongoDB client.
* @param database the name of the default database to return on
* {@link MongoDbFactory#getDb()}.
* @return new instance of {@link MongoDbFactory} suitable for the first available
* MongoDB client.
*/
MongoDbFactory getFor(String database) {
Object client = findAvailableClientProvider();
if (client instanceof MongoClient) {
return new SimpleMongoDbFactory(MongoClient.class.cast(client), database);
}
if (client instanceof com.mongodb.client.MongoClient) {
return new SimpleMongoClientDbFactory(
com.mongodb.client.MongoClient.class.cast(client), database);
}
return null;
}
private Object findAvailableClientProvider() {
for (ObjectProvider<?> provider : this.clientProviders) {
Object client = provider.getIfAvailable();
if (client != null) {
return client;
}
}
throw new IllegalStateException(
"Expected to find at least one MongoDB client.");
}
}
}

@ -65,7 +65,8 @@ public class MongoAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingBean(type = { "com.mongodb.MongoClient",
"com.mongodb.client.MongoClient" })
public MongoClient mongo() {
this.mongo = this.factory.createMongoClient(this.options);
return this.mongo;

@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.Set;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoClients;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
@ -39,7 +40,9 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
@ -173,6 +176,16 @@ public class MongoDataAutoConfigurationTests {
.doesNotHaveBean(MongoDataAutoConfiguration.class));
}
@Test
public void createsMongoDbFactoryForMongoClientClientWhenBeanPresent() {
this.contextRunner.withUserConfiguration(WithMongoClientClientConfiguration.class)
.run((context) -> {
MongoDbFactory dbFactory = context.getBean(MongoDbFactory.class);
assertThat(dbFactory).isInstanceOf(SimpleMongoClientDbFactory.class);
});
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static void assertDomainTypesDiscovered(MongoMappingContext mappingContext,
Class<?>... types) {
@ -197,6 +210,16 @@ public class MongoDataAutoConfigurationTests {
}
@Configuration
static class WithMongoClientClientConfiguration {
@Bean
com.mongodb.client.MongoClient mongoClient() {
return MongoClients.create();
}
}
private static class MyConverter implements Converter<MongoClient, Boolean> {
@Override

@ -20,14 +20,17 @@ import javax.net.SocketFactory;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.client.MongoClients;
import org.junit.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock;
/**
@ -78,6 +81,17 @@ public class MongoAutoConfigurationTests {
});
}
@Test
public void doesNotCreateMongoClientWhenAlreadyDefined() {
this.contextRunner
.withPropertyValues("spring.data.mongodb.uri:mongodb://localhost/test")
.withUserConfiguration(ConfigurationWithClientMongoClient.class)
.run((context) -> assertThatExceptionOfType(
NoSuchBeanDefinitionException.class)
.isThrownBy(() -> context.getBean(MongoClient.class)));
}
@Configuration
static class OptionsConfig {
@ -104,4 +118,13 @@ public class MongoAutoConfigurationTests {
}
static class ConfigurationWithClientMongoClient {
@Bean
com.mongodb.client.MongoClient mongoClient() {
return MongoClients.create();
}
}
}

Loading…
Cancel
Save