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
This commit is contained in:
parent
19d17e7ec9
commit
d549e6001a
|
@ -16,27 +16,35 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.data.mongo;
|
package org.springframework.boot.autoconfigure.data.mongo;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.mongodb.ClientSessionOptions;
|
import com.mongodb.ClientSessionOptions;
|
||||||
import com.mongodb.DB;
|
import com.mongodb.DB;
|
||||||
import com.mongodb.MongoClient;
|
import com.mongodb.MongoClient;
|
||||||
import com.mongodb.client.ClientSession;
|
import com.mongodb.client.ClientSession;
|
||||||
import com.mongodb.client.MongoDatabase;
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
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.ConditionalOnBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
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.MongoAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
|
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.dao.DataAccessException;
|
||||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||||
import org.springframework.data.mongodb.MongoDbFactory;
|
import org.springframework.data.mongodb.MongoDbFactory;
|
||||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
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.SimpleMongoDbFactory;
|
||||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||||
|
@ -63,11 +71,12 @@ import org.springframework.util.StringUtils;
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Eddú Meléndez
|
* @author Eddú Meléndez
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Christoph Strobl
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass({ MongoClient.class, MongoTemplate.class })
|
@ConditionalOnClass({ MongoClient.class, MongoTemplate.class })
|
||||||
@ConditionalOnBean(MongoClient.class)
|
@Conditional(AnySyncMongoClientAvailable.class)
|
||||||
@EnableConfigurationProperties(MongoProperties.class)
|
@EnableConfigurationProperties(MongoProperties.class)
|
||||||
@Import(MongoDataConfiguration.class)
|
@Import(MongoDataConfiguration.class)
|
||||||
@AutoConfigureAfter(MongoAutoConfiguration.class)
|
@AutoConfigureAfter(MongoAutoConfiguration.class)
|
||||||
|
@ -75,15 +84,22 @@ public class MongoDataAutoConfiguration {
|
||||||
|
|
||||||
private final MongoProperties properties;
|
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.properties = properties;
|
||||||
|
this.dbFactoryFactory = new MongoDbFactoryFactory(mongoClientProvider,
|
||||||
|
mongoClientClientProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@Conditional(AnySyncMongoClientAvailable.class)
|
||||||
@ConditionalOnMissingBean(MongoDbFactory.class)
|
@ConditionalOnMissingBean(MongoDbFactory.class)
|
||||||
public SimpleMongoDbFactory mongoDbFactory(MongoClient mongo) {
|
public MongoDbFactory mongoDbFactory() {
|
||||||
String database = this.properties.getMongoClientDatabase();
|
return this.dbFactoryFactory.getFor(this.properties.getMongoClientDatabase());
|
||||||
return new SimpleMongoDbFactory(mongo, database);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@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
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean(type = { "com.mongodb.MongoClient",
|
||||||
|
"com.mongodb.client.MongoClient" })
|
||||||
public MongoClient mongo() {
|
public MongoClient mongo() {
|
||||||
this.mongo = this.factory.createMongoClient(this.options);
|
this.mongo = this.factory.createMongoClient(this.options);
|
||||||
return this.mongo;
|
return this.mongo;
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Arrays;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.mongodb.MongoClient;
|
import com.mongodb.MongoClient;
|
||||||
|
import com.mongodb.client.MongoClients;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
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.CamelCaseAbbreviatingFieldNamingStrategy;
|
||||||
import org.springframework.data.mapping.model.FieldNamingStrategy;
|
import org.springframework.data.mapping.model.FieldNamingStrategy;
|
||||||
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
|
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.MongoTemplate;
|
||||||
|
import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory;
|
||||||
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
|
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
|
||||||
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
|
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
|
||||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||||
|
@ -173,6 +176,16 @@ public class MongoDataAutoConfigurationTests {
|
||||||
.doesNotHaveBean(MongoDataAutoConfiguration.class));
|
.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" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
private static void assertDomainTypesDiscovered(MongoMappingContext mappingContext,
|
private static void assertDomainTypesDiscovered(MongoMappingContext mappingContext,
|
||||||
Class<?>... types) {
|
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> {
|
private static class MyConverter implements Converter<MongoClient, Boolean> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,14 +20,17 @@ import javax.net.SocketFactory;
|
||||||
|
|
||||||
import com.mongodb.MongoClient;
|
import com.mongodb.MongoClient;
|
||||||
import com.mongodb.MongoClientOptions;
|
import com.mongodb.MongoClientOptions;
|
||||||
|
import com.mongodb.client.MongoClients;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.mockito.Mockito.mock;
|
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
|
@Configuration
|
||||||
static class OptionsConfig {
|
static class OptionsConfig {
|
||||||
|
|
||||||
|
@ -104,4 +118,13 @@ public class MongoAutoConfigurationTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ConfigurationWithClientMongoClient {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
com.mongodb.client.MongoClient mongoClient() {
|
||||||
|
return MongoClients.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue