Harmonize mongo auto-configuration

Provides a way to easily exclude `MongoAutoConfiguration` or
`MongoReactiveAutoConfiguration` so that multiple mongo clients
are not present.

Fixes gh-12407
This commit is contained in:
Madhura Bhave 2018-07-11 13:15:08 -07:00
parent 1ea91ece23
commit 6d9fabf5bc
6 changed files with 131 additions and 41 deletions

View File

@ -16,30 +16,25 @@
package org.springframework.boot.autoconfigure.data.mongo;
import java.util.Collections;
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.BeanUtils;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
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.domain.EntityScanner;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
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.annotation.Persistent;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
@ -48,7 +43,6 @@ import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.util.Assert;
@ -73,17 +67,15 @@ import org.springframework.util.StringUtils;
*/
@Configuration
@ConditionalOnClass({ MongoClient.class, MongoTemplate.class })
@ConditionalOnBean(MongoClient.class)
@EnableConfigurationProperties(MongoProperties.class)
@Import(MongoDataConfiguration.class)
@AutoConfigureAfter(MongoAutoConfiguration.class)
public class MongoDataAutoConfiguration {
private final ApplicationContext applicationContext;
private final MongoProperties properties;
public MongoDataAutoConfiguration(ApplicationContext applicationContext,
MongoProperties properties) {
this.applicationContext = applicationContext;
public MongoDataAutoConfiguration(MongoProperties properties) {
this.properties = properties;
}
@ -112,22 +104,6 @@ public class MongoDataAutoConfiguration {
return mappingConverter;
}
@Bean
@ConditionalOnMissingBean
public MongoMappingContext mongoMappingContext(MongoCustomConversions conversions)
throws ClassNotFoundException {
MongoMappingContext context = new MongoMappingContext();
context.setInitialEntitySet(new EntityScanner(this.applicationContext)
.scan(Document.class, Persistent.class));
Class<?> strategyClass = this.properties.getFieldNamingStrategy();
if (strategyClass != null) {
context.setFieldNamingStrategy(
(FieldNamingStrategy) BeanUtils.instantiateClass(strategyClass));
}
context.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
return context;
}
@Bean
@ConditionalOnMissingBean
public GridFsTemplate gridFsTemplate(MongoDbFactory mongoDbFactory,
@ -137,12 +113,6 @@ public class MongoDataAutoConfiguration {
mongoTemplate.getConverter());
}
@Bean
@ConditionalOnMissingBean
public MongoCustomConversions mongoCustomConversions() {
return new MongoCustomConversions(Collections.emptyList());
}
/**
* {@link MongoDbFactory} decorator to respect
* {@link MongoProperties#getGridFsDatabase()} if set.

View File

@ -0,0 +1,73 @@
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.data.mongo;
import java.util.Collections;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.domain.EntityScanner;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
/**
* Base configuration class for Spring Data's mongo support.
*
* @author Madhura Bhave
*/
@Configuration
class MongoDataConfiguration {
private final ApplicationContext applicationContext;
private final MongoProperties properties;
MongoDataConfiguration(ApplicationContext applicationContext,
MongoProperties properties) {
this.applicationContext = applicationContext;
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public MongoMappingContext mongoMappingContext(MongoCustomConversions conversions)
throws ClassNotFoundException {
MongoMappingContext context = new MongoMappingContext();
context.setInitialEntitySet(new EntityScanner(this.applicationContext)
.scan(Document.class, Persistent.class));
Class<?> strategyClass = this.properties.getFieldNamingStrategy();
if (strategyClass != null) {
context.setFieldNamingStrategy(
(FieldNamingStrategy) BeanUtils.instantiateClass(strategyClass));
}
context.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
return context;
}
@Bean
@ConditionalOnMissingBean
public MongoCustomConversions mongoCustomConversions() {
return new MongoCustomConversions(Collections.emptyList());
}
}

View File

@ -20,6 +20,7 @@ import com.mongodb.reactivestreams.client.MongoClient;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
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.mongo.MongoProperties;
@ -27,10 +28,15 @@ import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfigurati
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data's reactive mongo
@ -47,9 +53,10 @@ import org.springframework.data.mongodb.core.convert.MongoConverter;
*/
@Configuration
@ConditionalOnClass({ MongoClient.class, ReactiveMongoTemplate.class })
@ConditionalOnBean(MongoClient.class)
@EnableConfigurationProperties(MongoProperties.class)
@AutoConfigureAfter({ MongoReactiveAutoConfiguration.class,
MongoDataAutoConfiguration.class })
@Import(MongoDataConfiguration.class)
@AutoConfigureAfter(MongoReactiveAutoConfiguration.class)
public class MongoReactiveDataAutoConfiguration {
private final MongoProperties properties;
@ -74,4 +81,14 @@ public class MongoReactiveDataAutoConfiguration {
return new ReactiveMongoTemplate(reactiveMongoDatabaseFactory, converter);
}
@Bean
@ConditionalOnMissingBean(MongoConverter.class)
public MappingMongoConverter mappingMongoConverter(MongoMappingContext context,
MongoCustomConversions conversions) {
MappingMongoConverter mappingConverter = new MappingMongoConverter(
NoOpDbRefResolver.INSTANCE, context);
mappingConverter.setCustomConversions(conversions);
return mappingConverter;
}
}

View File

@ -24,6 +24,7 @@ import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.connection.netty.NettyStreamFactoryFactory;
import com.mongodb.reactivestreams.client.MongoClient;
import io.netty.channel.socket.SocketChannel;
import reactor.core.publisher.Flux;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -44,7 +45,7 @@ import org.springframework.core.env.Environment;
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(MongoClient.class)
@ConditionalOnClass({ MongoClient.class, Flux.class })
@EnableConfigurationProperties(MongoProperties.class)
public class MongoReactiveAutoConfiguration {

View File

@ -22,9 +22,12 @@ import java.util.Set;
import com.mongodb.MongoClient;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.city.City;
@ -60,6 +63,9 @@ public class MongoDataAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@Rule
public ExpectedException thrown = ExpectedException.none();
@After
public void close() {
if (this.context != null) {
@ -162,6 +168,15 @@ public class MongoDataAutoConfigurationTests {
assertThat(dateProperty.isEntity()).isFalse();
}
@Test
public void backsOffIfMongoClientBeanIsNotPresent() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(MongoDataAutoConfiguration.class);
this.context.refresh();
this.thrown.expect(NoSuchBeanDefinitionException.class);
assertThat(this.context.getBean(MongoDataAutoConfiguration.class)).isNull();
}
public void testFieldNamingStrategy(String strategy,
Class<? extends FieldNamingStrategy> expectedType) {
this.context = new AnnotationConfigApplicationContext();

View File

@ -17,10 +17,12 @@
package org.springframework.boot.autoconfigure.data.mongo;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
@ -36,6 +38,9 @@ public class MongoReactiveDataAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@Rule
public ExpectedException thrown = ExpectedException.none();
@After
public void close() {
if (this.context != null) {
@ -46,11 +51,20 @@ public class MongoReactiveDataAutoConfigurationTests {
@Test
public void templateExists() {
this.context = new AnnotationConfigApplicationContext(
PropertyPlaceholderAutoConfiguration.class, MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class, MongoReactiveAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
MongoReactiveAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class);
assertThat(this.context.getBeanNamesForType(ReactiveMongoTemplate.class))
.hasSize(1);
}
@Test
public void backsOffIfMongoClientBeanIsNotPresent() {
this.context = new AnnotationConfigApplicationContext(
PropertyPlaceholderAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class);
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.context.getBean(MongoReactiveDataAutoConfiguration.class);
}
}