diff --git a/spring-data-mongodb-parent/pom.xml b/spring-data-mongodb-parent/pom.xml
index d32c57501..68400bb5a 100644
--- a/spring-data-mongodb-parent/pom.xml
+++ b/spring-data-mongodb-parent/pom.xml
@@ -19,7 +19,7 @@
3.0.7.RELEASE
4.0.0.RELEASE
[${org.springframework.version.30}, ${org.springframework.version.40})
- 1.2.0.RELEASE
+ 1.3.0.BUILD-SNAPSHOT
1.6.11.RELEASE
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java
index a93d30f0a..43677a238 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
+import org.springframework.data.convert.EntityInstantiators;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToObjectIdConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToBigIntegerConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToStringConverter;
@@ -39,6 +40,7 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
protected final GenericConversionService conversionService;
protected CustomConversions conversions = new CustomConversions();
+ protected EntityInstantiators instantiators = new EntityInstantiators();
/**
* Creates a new {@link AbstractMongoConverter} using the given {@link GenericConversionService}.
@@ -60,6 +62,15 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
this.conversions = conversions;
}
+ /**
+ * Registers {@link EntityInstantiators} to customize entity instantiation.
+ *
+ * @param instantiators
+ */
+ public void setInstantiators(EntityInstantiators instantiators) {
+ this.instantiators = instantiators == null ? new EntityInstantiators() : instantiators;
+ }
+
/**
* Registers additional converters that will be available when using the {@link ConversionService} directly (e.g. for
* id conversion). These converters are not custom conversions as they'd introduce unwanted conversions (e.g.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java
deleted file mode 100644
index bc1f01152..000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappedConstructor.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright 2012 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.data.mongodb.core.convert;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import org.springframework.data.mapping.PersistentEntity;
-import org.springframework.data.mapping.PersistentProperty;
-import org.springframework.data.mapping.PreferredConstructor;
-import org.springframework.data.mapping.PreferredConstructor.Parameter;
-import org.springframework.data.mapping.PropertyPath;
-import org.springframework.data.mapping.context.MappingContext;
-import org.springframework.data.mapping.context.PersistentPropertyPath;
-import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
-import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
-import org.springframework.data.util.TypeInformation;
-import org.springframework.util.Assert;
-
-/**
- * Abstraction for a {@link PreferredConstructor} alongside mapping information.
- *
- * @author Oliver Gierke
- */
-class MappedConstructor {
-
- private final Set parameters;
-
- /**
- * Creates a new {@link MappedConstructor} from the given {@link MongoPersistentEntity} and {@link MappingContext}.
- *
- * @param entity must not be {@literal null}.
- * @param context must not be {@literal null}.
- */
- public MappedConstructor(MongoPersistentEntity> entity,
- MappingContext extends MongoPersistentEntity>, MongoPersistentProperty> context) {
-
- Assert.notNull(entity);
- Assert.notNull(context);
-
- this.parameters = new HashSet();
-
- for (Parameter> parameter : entity.getPreferredConstructor().getParameters()) {
- parameters.add(new MappedParameter(parameter, entity, context));
- }
- }
-
- /**
- * Returns whether the given {@link PersistentProperty} is referenced in a constructor argument of the
- * {@link PersistentEntity} backing this {@link MappedConstructor}.
- *
- * @param property must not be {@literal null}.
- * @return
- */
- public boolean isConstructorParameter(PersistentProperty> property) {
-
- Assert.notNull(property);
-
- for (MappedConstructor.MappedParameter parameter : parameters) {
- if (parameter.maps(property)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Returns the {@link MappedParameter} for the given {@link Parameter}.
- *
- * @param parameter must not be {@literal null}.
- * @return
- */
- public MappedParameter getFor(Parameter> parameter) {
-
- for (MappedParameter mappedParameter : parameters) {
- if (mappedParameter.parameter.equals(parameter)) {
- return mappedParameter;
- }
- }
-
- throw new IllegalStateException(String.format("Didn't find a MappedParameter for %s!", parameter.toString()));
- }
-
- /**
- * Abstraction of a {@link Parameter} alongside mapping information.
- *
- * @author Oliver Gierke
- */
- static class MappedParameter {
-
- private final MongoPersistentProperty property;
- private final Parameter> parameter;
-
- /**
- * Creates a new {@link MappedParameter} for the given {@link Parameter}, {@link MongoPersistentProperty} and
- * {@link MappingContext}.
- *
- * @param parameter must not be {@literal null}.
- * @param entity must not be {@literal null}.
- * @param context must not be {@literal null}.
- */
- public MappedParameter(Parameter> parameter, MongoPersistentEntity> entity,
- MappingContext extends MongoPersistentEntity>, ? extends MongoPersistentProperty> context) {
-
- Assert.notNull(parameter);
- Assert.notNull(entity);
- Assert.notNull(context);
-
- this.parameter = parameter;
-
- PropertyPath propertyPath = PropertyPath.from(parameter.getName(), entity.getType());
- PersistentPropertyPath extends MongoPersistentProperty> path = context.getPersistentPropertyPath(propertyPath);
- this.property = path == null ? null : path.getLeafProperty();
- }
-
- /**
- * Returns whether there is a SpEL expression configured for this parameter.
- *
- * @return
- */
- public boolean hasSpELExpression() {
- return parameter.getKey() != null;
- }
-
- /**
- * Returns the field name to be used to lookup the value which in turn shall be converted into the constructor
- * parameter.
- *
- * @return
- */
- public String getFieldName() {
- return property.getFieldName();
- }
-
- /**
- * Returns the type of the property backing the {@link Parameter}.
- *
- * @return
- */
- public TypeInformation> getPropertyTypeInformation() {
- return property.getTypeInformation();
- }
-
- /**
- * Returns whether the given {@link PersistentProperty} is mapped by the parameter.
- *
- * @param property
- * @return
- */
- public boolean maps(PersistentProperty> property) {
- return this.property.equals(property);
- }
- }
-}
\ No newline at end of file
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
index 1df55a411..3e118cf0e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
@@ -15,7 +15,6 @@
*/
package org.springframework.data.mongodb.core.convert;
-import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -30,29 +29,30 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
-import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.support.ConversionServiceFactory;
+import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.convert.TypeMapper;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
-import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
+import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.ParameterValueProvider;
-import org.springframework.data.mapping.model.SpELAwareParameterValueProvider;
+import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
+import org.springframework.data.mapping.model.PropertyValueProvider;
+import org.springframework.data.mapping.model.SpELContext;
+import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
-import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -82,6 +82,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
protected boolean useFieldAccessOnly = true;
protected MongoTypeMapper typeMapper;
+ private SpELContext spELContext;
+
/**
* Creates a new {@link MappingMongoConverter} given the new {@link MongoDbFactory} and {@link MappingContext}.
*
@@ -101,6 +103,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
this.mappingContext = mappingContext;
this.typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, mappingContext);
this.idMapper = new QueryMapper(this);
+
+ this.spELContext = new SpELContext(DBObjectPropertyAccessor.INSTANCE);
}
/**
@@ -140,7 +144,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+
this.applicationContext = applicationContext;
+ this.spELContext = new SpELContext(this.spELContext, applicationContext);
}
/*
@@ -187,34 +193,39 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return read(persistentEntity, dbo);
}
+ private ParameterValueProvider getParameterProvider(MongoPersistentEntity> entity,
+ DBObject source, DefaultSpELExpressionEvaluator evaluator) {
+
+ MongoDbPropertyValueProvider provider = new MongoDbPropertyValueProvider(source, evaluator);
+ PersistentEntityParameterValueProvider parameterProvider = new PersistentEntityParameterValueProvider(
+ entity, provider);
+ parameterProvider.setSpELEvaluator(evaluator);
+
+ return parameterProvider;
+ }
+
private S read(final MongoPersistentEntity entity, final DBObject dbo) {
- final StandardEvaluationContext spelCtx = new StandardEvaluationContext(dbo);
- spelCtx.addPropertyAccessor(DBObjectPropertyAccessor.INSTANCE);
+ final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(dbo, spELContext);
- if (applicationContext != null) {
- spelCtx.setBeanResolver(new BeanFactoryResolver(applicationContext));
- }
+ ParameterValueProvider provider = getParameterProvider(entity, dbo, evaluator);
+ EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
+ S instance = instantiator.createInstance(entity, provider);
- final MappedConstructor constructor = new MappedConstructor(entity, mappingContext);
-
- SpELAwareParameterValueProvider delegate = new SpELAwareParameterValueProvider(spelExpressionParser, spelCtx);
- ParameterValueProvider provider = new DelegatingParameterValueProvider(constructor, dbo, delegate);
-
- final BeanWrapper, S> wrapper = BeanWrapper.create(entity, provider, conversionService);
+ final BeanWrapper, S> wrapper = BeanWrapper.create(instance, conversionService);
// Set properties not already set in the constructor
entity.doWithProperties(new PropertyHandler() {
public void doWithPersistentProperty(MongoPersistentProperty prop) {
- boolean isConstructorProperty = constructor.isConstructorParameter(prop);
+ boolean isConstructorProperty = entity.isConstructorArgument(prop);
boolean hasValueForProperty = dbo.containsField(prop.getFieldName());
if (!hasValueForProperty || isConstructorProperty) {
return;
}
- Object obj = getValueInternal(prop, dbo, spelCtx, prop.getSpelExpression());
+ Object obj = getValueInternal(prop, dbo, evaluator);
wrapper.setProperty(prop, obj, useFieldAccessOnly);
}
});
@@ -223,7 +234,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
entity.doWithAssociations(new AssociationHandler() {
public void doWithAssociation(Association association) {
MongoPersistentProperty inverseProp = association.getInverse();
- Object obj = getValueInternal(inverseProp, dbo, spelCtx, inverseProp.getSpelExpression());
+ Object obj = getValueInternal(inverseProp, dbo, evaluator);
try {
wrapper.setProperty(inverseProp, obj);
} catch (IllegalAccessException e) {
@@ -621,58 +632,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return new DBRef(db, collection, idMapper.convertId(id));
}
- @SuppressWarnings("unchecked")
- protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, StandardEvaluationContext ctx,
- String spelExpr) {
+ protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, SpELExpressionEvaluator eval) {
- Object o;
- if (null != spelExpr) {
- Expression x = spelExpressionParser.parseExpression(spelExpr);
- o = x.getValue(ctx);
- } else {
-
- Object sourceValue = dbo.get(prop.getFieldName());
-
- if (sourceValue == null) {
- return null;
- }
-
- Class> propertyType = prop.getType();
-
- if (conversions.hasCustomReadTarget(sourceValue.getClass(), propertyType)) {
- return conversionService.convert(sourceValue, propertyType);
- }
-
- if (sourceValue instanceof DBRef) {
- sourceValue = ((DBRef) sourceValue).fetch();
- }
- if (sourceValue instanceof DBObject) {
- if (prop.isMap()) {
- return readMap(prop.getTypeInformation(), (DBObject) sourceValue);
- } else if (prop.isArray() && sourceValue instanceof BasicDBObject
- && ((DBObject) sourceValue).keySet().size() == 0) {
- // It's empty
- return Array.newInstance(prop.getComponentType(), 0);
- } else if (prop.isCollectionLike() && sourceValue instanceof BasicDBList) {
- return readCollectionOrArray((TypeInformation extends Collection>>) prop.getTypeInformation(),
- (BasicDBList) sourceValue);
- }
-
- TypeInformation> toType = typeMapper.readType((DBObject) sourceValue);
-
- // It's a complex object, have to read it in
- if (toType != null) {
- // TODO: why do we remove the type?
- // dbo.removeField(CUSTOM_TYPE_KEY);
- o = read(toType, (DBObject) sourceValue);
- } else {
- o = read(mappingContext.getPersistentEntity(prop.getTypeInformation()), (DBObject) sourceValue);
- }
- } else {
- o = sourceValue;
- }
- }
- return o;
+ MongoDbPropertyValueProvider provider = new MongoDbPropertyValueProvider(dbo, spELContext);
+ return provider.getPropertyValue(prop);
}
/**
@@ -858,46 +821,42 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return dbObject;
}
- private class DelegatingParameterValueProvider implements ParameterValueProvider {
+ private class MongoDbPropertyValueProvider implements PropertyValueProvider {
private final DBObject source;
- private final ParameterValueProvider delegate;
- private final MappedConstructor constructor;
+ private final SpELExpressionEvaluator evaluator;
- /**
- * {@link ParameterValueProvider} to delegate source object lookup to a {@link SpELAwareParameterValueProvider} in
- * case a MappCon
- *
- * @param constructor must not be {@literal null}.
- * @param source must not be {@literal null}.
- * @param delegate must not be {@literal null}.
- */
- public DelegatingParameterValueProvider(MappedConstructor constructor, DBObject source,
- SpELAwareParameterValueProvider delegate) {
+ public MongoDbPropertyValueProvider(DBObject source, SpELContext factory) {
+ this(source, new DefaultSpELExpressionEvaluator(source, factory));
+ }
+
+ public MongoDbPropertyValueProvider(DBObject source, DefaultSpELExpressionEvaluator evaluator) {
- Assert.notNull(constructor);
Assert.notNull(source);
- Assert.notNull(delegate);
+ Assert.notNull(evaluator);
- this.constructor = constructor;
this.source = source;
- this.delegate = delegate;
+ this.evaluator = evaluator;
}
/*
* (non-Javadoc)
- * @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
+ * @see org.springframework.data.convert.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@SuppressWarnings("unchecked")
- public T getParameterValue(Parameter parameter) {
+ public T getPropertyValue(MongoPersistentProperty property) {
- MappedConstructor.MappedParameter mappedParameter = constructor.getFor(parameter);
- Object value = mappedParameter.hasSpELExpression() ? delegate.getParameterValue(parameter) : source
- .get(mappedParameter.getFieldName());
+ String expression = property.getSpelExpression();
+ TypeInformation> type = property.getTypeInformation();
+ Object value = expression != null ? evaluator.evaluate(expression) : source.get(property.getFieldName());
- TypeInformation> type = mappedParameter.getPropertyTypeInformation();
+ if (value == null) {
+ return null;
+ }
- if (value instanceof DBRef) {
+ if (conversions.hasCustomReadTarget(value.getClass(), type.getType())) {
+ return (T) conversionService.convert(value, type.getType());
+ } else if (value instanceof DBRef) {
return (T) read(type, ((DBRef) value).fetch());
} else if (value instanceof BasicDBList) {
return (T) getPotentiallyConvertedSimpleRead(readCollectionOrArray(type, (BasicDBList) value), type.getType());