DATAMONGO-412 - General overhaul of the JavaConfig base class.

Refactored JavaConfig base class to reflect the XML namespace a bit more closely (esp. regarding configuration of custom converters and thus registering "simple" types). Prevent duplicate invocation of getUserCredentials(). Added JavaDoc to explain which configuration methods use which other ones to ease detailed configuration.
This commit is contained in:
Oliver Gierke
2012-03-08 13:10:50 +01:00
parent 9f2b1f4ed0
commit 51a469b46f
2 changed files with 128 additions and 38 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2011 the original author or authors. * Copyright 2011-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -15,88 +15,176 @@
*/ */
package org.springframework.data.mongodb.config; package org.springframework.data.mongodb.config;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import com.mongodb.Mongo;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent; import org.springframework.data.annotation.Persistent;
import org.springframework.data.authentication.UserCredentials; import org.springframework.data.authentication.UserCredentials;
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.SimpleMongoDbFactory; import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import com.mongodb.Mongo;
/**
* Base class for Spring Data Mongo configuration using JavaConfig.
*
* @author Mark Pollack
* @author Oliver Gierke
*/
@Configuration @Configuration
public abstract class AbstractMongoConfiguration { public abstract class AbstractMongoConfiguration {
public abstract String getDatabaseName(); /**
* Return the name of the database to connect to.
*
* @return must not be {@literal null}.
*/
protected abstract String getDatabaseName();
/**
* Return the {@link Mongo} instance to connect to.
*
* @return
* @throws Exception
*/
@Bean @Bean
public abstract Mongo mongo() throws Exception; public abstract Mongo mongo() throws Exception;
/**
* Creates a {@link MongoTemplate}.
*
* @return
* @throws Exception
*/
@Bean @Bean
public MongoTemplate mongoTemplate() throws Exception { public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory(), mappingMongoConverter()); return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
} }
/**
* Creates a {@link SimpleMongoDbFactory} to be used by the {@link MongoTemplate}. Will use the {@link Mongo} instance
* configured in {@link #mongo()}.
*
* @see #mongo()
* @see #mongoTemplate()
* @return
* @throws Exception
*/
@Bean @Bean
public MongoDbFactory mongoDbFactory() throws Exception { public SimpleMongoDbFactory mongoDbFactory() throws Exception {
if (getUserCredentials() == null) {
UserCredentials creadentials = getUserCredentials();
if (creadentials == null) {
return new SimpleMongoDbFactory(mongo(), getDatabaseName()); return new SimpleMongoDbFactory(mongo(), getDatabaseName());
} else { } else {
return new SimpleMongoDbFactory(mongo(), getDatabaseName(), getUserCredentials()); return new SimpleMongoDbFactory(mongo(), getDatabaseName(), creadentials);
} }
} }
public String getMappingBasePackage() { /**
return ""; * Return the base package to scan for mapped {@link Document}s.
} *
* @return
public UserCredentials getUserCredentials() { */
protected String getMappingBasePackage() {
return null; return null;
} }
/**
* Return {@link UserCredentials} to be used when connecting to the MongoDB instance or {@literal null} if none shall
* be used.
*
* @return
*/
protected UserCredentials getUserCredentials() {
return null;
}
/**
* Creates a {@link MongoMappingContext} equipped with entity classes scanned from the mapping base package.
*
* @see #getMappingBasePackage()
* @return
* @throws ClassNotFoundException
*/
@Bean @Bean
public MongoMappingContext mongoMappingContext() throws ClassNotFoundException, LinkageError { public MongoMappingContext mongoMappingContext() throws ClassNotFoundException {
MongoMappingContext mappingContext = new MongoMappingContext(); MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder());
mappingContext.afterPropertiesSet();
return mappingContext;
}
/**
* Register custom {@link Converter}s in a {@link CustomConversions} object if required. These
* {@link CustomConversions} will be registered with the {@link #mappingMongoConverter()} and
* {@link #mongoMappingContext()}. Returns an empty {@link CustomConversions} instance by default.
*
* @return must not be {@literal null}.
*/
@Bean
public CustomConversions customConversions() {
return new CustomConversions(Collections.emptyList());
}
/**
* Creates a {@link MappingMongoConverter} using the configured {@link #mongoDbFactory()} and
* {@link #mongoMappingContext()}. Will get {@link #customConversions()} applied.
*
* @see #customConversions()
* @see #mongoMappingContext()
* @see #mongoDbFactory()
* @return
* @throws Exception
*/
@Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory(), mongoMappingContext());
converter.setCustomConversions(customConversions());
return converter;
}
/**
* Scans the mapping base package for classes annotated with {@link Document}.
*
* @see #getMappingBasePackage()
* @return
* @throws ClassNotFoundException
*/
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
String basePackage = getMappingBasePackage(); String basePackage = getMappingBasePackage();
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
if (StringUtils.hasText(basePackage)) { if (StringUtils.hasText(basePackage)) {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider( ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false); false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class)); componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(), mappingContext.getClass() initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
.getClassLoader())); AbstractMongoConfiguration.class.getClassLoader()));
} }
mappingContext.setInitialEntitySet(initialEntitySet);
}
return mappingContext;
} }
@Bean return initialEntitySet;
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory(), mongoMappingContext());
afterMappingMongoConverterCreation(converter);
return converter;
}
/**
* Hook that allows post-processing after the MappingMongoConverter has been successfully created.
*
* @param converter
*/
protected void afterMappingMongoConverterCreation(MappingMongoConverter converter) {
} }
} }

View File

@@ -2,12 +2,14 @@ package org.springframework.data.mongodb.core;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.mongodb.Mongo;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration; import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.core.convert.CustomConversions; import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import com.mongodb.Mongo;
public class TestMongoConfiguration extends AbstractMongoConfiguration { public class TestMongoConfiguration extends AbstractMongoConfiguration {
@@ -24,15 +26,15 @@ public class TestMongoConfiguration extends AbstractMongoConfiguration {
@Override @Override
public String getMappingBasePackage() { public String getMappingBasePackage() {
return "org.springframework.data.mongodb.core.core.mapping"; return MongoMappingContext.class.getPackage().getName();
} }
@Override @Override
protected void afterMappingMongoConverterCreation(MappingMongoConverter converter) { public CustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>(); List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(new org.springframework.data.mongodb.core.PersonReadConverter()); converters.add(new org.springframework.data.mongodb.core.PersonReadConverter());
converters.add(new org.springframework.data.mongodb.core.PersonWriteConverter()); converters.add(new org.springframework.data.mongodb.core.PersonWriteConverter());
converter.setCustomConversions(new CustomConversions(converters)); return new CustomConversions(converters);
} }
} }