Skip to content

Commit 4203fd4

Browse files
committed
Add support for Value Expressions.
Closes #4634
1 parent 03522e1 commit 4203fd4

12 files changed

+134
-63
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
*/
1616
package org.springframework.data.mongodb.core.convert;
1717

18+
import java.util.function.Function;
19+
1820
import org.bson.Document;
21+
1922
import org.springframework.data.mapping.PersistentPropertyAccessor;
2023
import org.springframework.data.mapping.context.MappingContext;
21-
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
2224
import org.springframework.data.mapping.model.SpELContext;
23-
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
25+
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
2426
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
2527
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
2628
import org.springframework.lang.Nullable;
@@ -37,6 +39,7 @@ class DefaultDbRefProxyHandler implements DbRefProxyHandler {
3739
private final SpELContext spELContext;
3840
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
3941
private final ValueResolver resolver;
42+
private final Function<Object, ValueExpressionEvaluator> evaluatorFactory;
4043

4144
/**
4245
* @param spELContext must not be {@literal null}.
@@ -45,11 +48,12 @@ class DefaultDbRefProxyHandler implements DbRefProxyHandler {
4548
*/
4649
public DefaultDbRefProxyHandler(SpELContext spELContext,
4750
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext,
48-
ValueResolver resolver) {
51+
ValueResolver resolver, Function<Object, ValueExpressionEvaluator> evaluatorFactory) {
4952

5053
this.spELContext = spELContext;
5154
this.mappingContext = mappingContext;
5255
this.resolver = resolver;
56+
this.evaluatorFactory = evaluatorFactory;
5357
}
5458

5559
@Override
@@ -66,7 +70,7 @@ public Object populateId(MongoPersistentProperty property, @Nullable DBRef sourc
6670
return proxy;
6771
}
6872

69-
SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(proxy, spELContext);
73+
ValueExpressionEvaluator evaluator = evaluatorFactory.apply(proxy);
7074
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(proxy);
7175

7276
Document object = new Document(idProperty.getFieldName(), source.getId());

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import org.bson.Document;
1919
import org.bson.conversions.Bson;
20-
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
20+
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
2121
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
2222

2323
/**
@@ -32,18 +32,18 @@ class DefaultDbRefResolverCallback implements DbRefResolverCallback {
3232
private final Bson surroundingObject;
3333
private final ObjectPath path;
3434
private final ValueResolver resolver;
35-
private final SpELExpressionEvaluator evaluator;
35+
private final ValueExpressionEvaluator evaluator;
3636

3737
/**
3838
* Creates a new {@link DefaultDbRefResolverCallback} using the given {@link Document}, {@link ObjectPath},
39-
* {@link ValueResolver} and {@link SpELExpressionEvaluator}.
39+
* {@link ValueResolver} and {@link ValueExpressionEvaluator}.
4040
*
4141
* @param surroundingObject must not be {@literal null}.
4242
* @param path must not be {@literal null}.
4343
* @param evaluator must not be {@literal null}.
4444
* @param resolver must not be {@literal null}.
4545
*/
46-
public DefaultDbRefResolverCallback(Bson surroundingObject, ObjectPath path, SpELExpressionEvaluator evaluator,
46+
public DefaultDbRefResolverCallback(Bson surroundingObject, ObjectPath path, ValueExpressionEvaluator evaluator,
4747
ValueResolver resolver) {
4848

4949
this.surroundingObject = surroundingObject;

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

+52-29
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.bson.conversions.Bson;
4040
import org.bson.json.JsonReader;
4141
import org.bson.types.ObjectId;
42+
4243
import org.springframework.beans.BeansException;
4344
import org.springframework.beans.factory.BeanClassLoaderAware;
4445
import org.springframework.context.ApplicationContext;
@@ -47,6 +48,9 @@
4748
import org.springframework.core.convert.ConversionService;
4849
import org.springframework.core.convert.TypeDescriptor;
4950
import org.springframework.core.convert.support.DefaultConversionService;
51+
import org.springframework.core.env.Environment;
52+
import org.springframework.core.env.EnvironmentCapable;
53+
import org.springframework.core.env.StandardEnvironment;
5054
import org.springframework.data.annotation.Reference;
5155
import org.springframework.data.convert.CustomConversions;
5256
import org.springframework.data.convert.TypeMapper;
@@ -59,15 +63,16 @@
5963
import org.springframework.data.mapping.PersistentPropertyAccessor;
6064
import org.springframework.data.mapping.callback.EntityCallbacks;
6165
import org.springframework.data.mapping.context.MappingContext;
66+
import org.springframework.data.mapping.model.CachingValueExpressionEvaluatorFactory;
6267
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
63-
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
6468
import org.springframework.data.mapping.model.EntityInstantiator;
6569
import org.springframework.data.mapping.model.ParameterValueProvider;
6670
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
6771
import org.springframework.data.mapping.model.PropertyValueProvider;
6872
import org.springframework.data.mapping.model.SpELContext;
69-
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
7073
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
74+
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
75+
import org.springframework.data.mapping.model.ValueExpressionParameterValueProvider;
7176
import org.springframework.data.mongodb.CodecRegistryProvider;
7277
import org.springframework.data.mongodb.MongoDatabaseFactory;
7378
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty;
@@ -88,6 +93,7 @@
8893
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
8994
import org.springframework.data.util.Predicates;
9095
import org.springframework.data.util.TypeInformation;
96+
import org.springframework.expression.spel.standard.SpelExpressionParser;
9197
import org.springframework.lang.Nullable;
9298
import org.springframework.util.Assert;
9399
import org.springframework.util.ClassUtils;
@@ -116,7 +122,8 @@
116122
* @author Divya Srivastava
117123
* @author Julia Lee
118124
*/
119-
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware {
125+
public class MappingMongoConverter extends AbstractMongoConverter
126+
implements ApplicationContextAware, EnvironmentCapable {
120127

121128
private static final String INCOMPATIBLE_TYPES = "Cannot convert %1$s of type %2$s into an instance of %3$s; Implement a custom Converter<%2$s, %3$s> and register it with the CustomConversions; Parent object was: %4$s";
122129
private static final String INVALID_TYPE_TO_READ = "Expected to read Document %s into type %s but didn't find a PersistentEntity for the latter";
@@ -132,15 +139,20 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
132139
protected final ReferenceLookupDelegate referenceLookupDelegate;
133140

134141
protected @Nullable ApplicationContext applicationContext;
142+
protected @Nullable Environment environment;
135143
protected MongoTypeMapper typeMapper;
136144
protected @Nullable String mapKeyDotReplacement = null;
137145
protected @Nullable CodecRegistryProvider codecRegistryProvider;
138146

139147
private MongoTypeMapper defaultTypeMapper;
140148
private SpELContext spELContext;
141149
private @Nullable EntityCallbacks entityCallbacks;
150+
private final SpelExpressionParser expressionParser = new SpelExpressionParser();
142151
private final DocumentPointerFactory documentPointerFactory;
143-
private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory();
152+
private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory(
153+
expressionParser);
154+
private final CachingValueExpressionEvaluatorFactory expressionEvaluatorFactory = new CachingValueExpressionEvaluatorFactory(
155+
expressionParser, this, o -> spELContext.getEvaluationContext(o));
144156

145157
/**
146158
* Creates a new {@link MappingMongoConverter} given the new {@link DbRefResolver} and {@link MappingContext}.
@@ -169,7 +181,7 @@ public MappingMongoConverter(DbRefResolver dbRefResolver,
169181

170182
ConversionContext context = getConversionContext(path);
171183
return MappingMongoConverter.this.getValueInternal(context, prop, bson, evaluator);
172-
});
184+
}, expressionEvaluatorFactory::create);
173185

174186
this.referenceLookupDelegate = new ReferenceLookupDelegate(mappingContext, spELContext);
175187
this.documentPointerFactory = new DocumentPointerFactory(conversionService, mappingContext);
@@ -271,9 +283,11 @@ public MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentPropert
271283
return mappingContext;
272284
}
273285

286+
@Override
274287
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
275288

276289
this.applicationContext = applicationContext;
290+
this.environment = applicationContext.getEnvironment();
277291
this.spELContext = new SpELContext(this.spELContext, applicationContext);
278292
this.projectionFactory.setBeanFactory(applicationContext);
279293
this.projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
@@ -288,6 +302,15 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
288302
}
289303
}
290304

305+
@Override
306+
public Environment getEnvironment() {
307+
308+
if (environment == null) {
309+
environment = new StandardEnvironment();
310+
}
311+
return environment;
312+
}
313+
291314
/**
292315
* Set the {@link EntityCallbacks} instance to use when invoking
293316
* {@link org.springframework.data.mapping.callback.EntityCallback callbacks} like the {@link AfterConvertCallback}.
@@ -328,7 +351,7 @@ private <R> R doReadProjection(ConversionContext context, Bson bson, EntityProje
328351
TypeInformation<?> mappedType = projection.getActualMappedType();
329352
MongoPersistentEntity<R> mappedEntity = (MongoPersistentEntity<R>) getMappingContext()
330353
.getPersistentEntity(mappedType);
331-
SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(bson, spELContext);
354+
ValueExpressionEvaluator evaluator = expressionEvaluatorFactory.create(bson);
332355

333356
boolean isInterfaceProjection = mappedType.getType().isInterface();
334357
if (isInterfaceProjection) {
@@ -481,14 +504,14 @@ protected <S extends Object> S readDocument(ConversionContext context, Bson bson
481504
}
482505

483506
private ParameterValueProvider<MongoPersistentProperty> getParameterProvider(ConversionContext context,
484-
MongoPersistentEntity<?> entity, DocumentAccessor source, SpELExpressionEvaluator evaluator) {
507+
MongoPersistentEntity<?> entity, DocumentAccessor source, ValueExpressionEvaluator evaluator) {
485508

486509
AssociationAwareMongoDbPropertyValueProvider provider = new AssociationAwareMongoDbPropertyValueProvider(context,
487510
source, evaluator);
488511
PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<>(
489512
entity, provider, context.getPath().getCurrentObject());
490513

491-
return new ConverterAwareSpELExpressionParameterValueProvider(context, evaluator, conversionService,
514+
return new ConverterAwareValueExpressionParameterValueProvider(context, evaluator, conversionService,
492515
parameterProvider);
493516
}
494517

@@ -499,7 +522,7 @@ private <S> S read(ConversionContext context, MongoPersistentEntity<S> entity, D
499522
return existing;
500523
}
501524

502-
SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(bson, spELContext);
525+
ValueExpressionEvaluator evaluator = expressionEvaluatorFactory.create(bson);
503526
DocumentAccessor documentAccessor = new DocumentAccessor(bson);
504527

505528
InstanceCreatorMetadata<MongoPersistentProperty> instanceCreatorMetadata = entity.getInstanceCreatorMetadata();
@@ -515,7 +538,7 @@ private <S> S read(ConversionContext context, MongoPersistentEntity<S> entity, D
515538
}
516539

517540
private <S> S populateProperties(ConversionContext context, MongoPersistentEntity<S> entity,
518-
DocumentAccessor documentAccessor, SpELExpressionEvaluator evaluator, S instance) {
541+
DocumentAccessor documentAccessor, ValueExpressionEvaluator evaluator, S instance) {
519542

520543
if (!entity.requiresPropertyPopulation()) {
521544
return instance;
@@ -545,7 +568,7 @@ private <S> S populateProperties(ConversionContext context, MongoPersistentEntit
545568
*/
546569
@Nullable
547570
private Object readAndPopulateIdentifier(ConversionContext context, PersistentPropertyAccessor<?> accessor,
548-
DocumentAccessor document, MongoPersistentEntity<?> entity, SpELExpressionEvaluator evaluator) {
571+
DocumentAccessor document, MongoPersistentEntity<?> entity, ValueExpressionEvaluator evaluator) {
549572

550573
Object rawId = document.getRawId(entity);
551574

@@ -565,7 +588,7 @@ private Object readAndPopulateIdentifier(ConversionContext context, PersistentPr
565588
}
566589

567590
@Nullable
568-
private Object readIdValue(ConversionContext context, SpELExpressionEvaluator evaluator,
591+
private Object readIdValue(ConversionContext context, ValueExpressionEvaluator evaluator,
569592
MongoPersistentProperty idProperty, Object rawId) {
570593

571594
String expression = idProperty.getSpelExpression();
@@ -578,7 +601,7 @@ private Object readIdValue(ConversionContext context, SpELExpressionEvaluator ev
578601

579602
private void readProperties(ConversionContext context, MongoPersistentEntity<?> entity,
580603
PersistentPropertyAccessor<?> accessor, DocumentAccessor documentAccessor,
581-
MongoDbPropertyValueProvider valueProvider, SpELExpressionEvaluator evaluator,
604+
MongoDbPropertyValueProvider valueProvider, ValueExpressionEvaluator evaluator,
582605
Predicate<MongoPersistentProperty> propertyFilter) {
583606

584607
DbRefResolverCallback callback = null;
@@ -622,7 +645,7 @@ private void readProperties(ConversionContext context, MongoPersistentEntity<?>
622645
}
623646

624647
private DbRefResolverCallback getDbRefResolverCallback(ConversionContext context, DocumentAccessor documentAccessor,
625-
SpELExpressionEvaluator evaluator) {
648+
ValueExpressionEvaluator evaluator) {
626649

627650
return new DefaultDbRefResolverCallback(documentAccessor.getDocument(), context.getPath(), evaluator,
628651
(prop, bson, e, path) -> MappingMongoConverter.this.getValueInternal(context, prop, bson, e));
@@ -1387,7 +1410,7 @@ protected DBRef createDBRef(Object target, @Nullable MongoPersistentProperty pro
13871410

13881411
@Nullable
13891412
private Object getValueInternal(ConversionContext context, MongoPersistentProperty prop, Bson bson,
1390-
SpELExpressionEvaluator evaluator) {
1413+
ValueExpressionEvaluator evaluator) {
13911414
return new MongoDbPropertyValueProvider(context, bson, evaluator).getPropertyValue(prop);
13921415
}
13931416

@@ -1876,35 +1899,35 @@ static class MongoDbPropertyValueProvider implements PropertyValueProvider<Mongo
18761899

18771900
final ConversionContext context;
18781901
final DocumentAccessor accessor;
1879-
final SpELExpressionEvaluator evaluator;
1902+
final ValueExpressionEvaluator evaluator;
18801903
final SpELContext spELContext;
18811904

18821905
/**
1883-
* Creates a new {@link MongoDbPropertyValueProvider} for the given source, {@link SpELExpressionEvaluator} and
1906+
* Creates a new {@link MongoDbPropertyValueProvider} for the given source, {@link ValueExpressionEvaluator} and
18841907
* {@link ObjectPath}.
18851908
*
18861909
* @param context must not be {@literal null}.
18871910
* @param source must not be {@literal null}.
18881911
* @param evaluator must not be {@literal null}.
18891912
*/
1890-
MongoDbPropertyValueProvider(ConversionContext context, Bson source, SpELExpressionEvaluator evaluator) {
1913+
MongoDbPropertyValueProvider(ConversionContext context, Bson source, ValueExpressionEvaluator evaluator) {
18911914
this(context, new DocumentAccessor(source), evaluator, null);
18921915
}
18931916

18941917
/**
1895-
* Creates a new {@link MongoDbPropertyValueProvider} for the given source, {@link SpELExpressionEvaluator} and
1918+
* Creates a new {@link MongoDbPropertyValueProvider} for the given source, {@link ValueExpressionEvaluator} and
18961919
* {@link ObjectPath}.
18971920
*
18981921
* @param context must not be {@literal null}.
18991922
* @param accessor must not be {@literal null}.
19001923
* @param evaluator must not be {@literal null}.
19011924
*/
19021925
MongoDbPropertyValueProvider(ConversionContext context, DocumentAccessor accessor,
1903-
SpELExpressionEvaluator evaluator, SpELContext spELContext) {
1926+
ValueExpressionEvaluator evaluator, SpELContext spELContext) {
19041927

19051928
Assert.notNull(context, "ConversionContext must no be null");
19061929
Assert.notNull(accessor, "DocumentAccessor must no be null");
1907-
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null");
1930+
Assert.notNull(evaluator, "ValueExpressionEvaluator must not be null");
19081931

19091932
this.context = context;
19101933
this.accessor = accessor;
@@ -1953,13 +1976,13 @@ class AssociationAwareMongoDbPropertyValueProvider extends MongoDbPropertyValueP
19531976

19541977
/**
19551978
* Creates a new {@link AssociationAwareMongoDbPropertyValueProvider} for the given source,
1956-
* {@link SpELExpressionEvaluator} and {@link ObjectPath}.
1979+
* {@link ValueExpressionEvaluator} and {@link ObjectPath}.
19571980
*
19581981
* @param source must not be {@literal null}.
19591982
* @param evaluator must not be {@literal null}.
19601983
*/
19611984
AssociationAwareMongoDbPropertyValueProvider(ConversionContext context, DocumentAccessor source,
1962-
SpELExpressionEvaluator evaluator) {
1985+
ValueExpressionEvaluator evaluator) {
19631986
super(context, source, evaluator, MappingMongoConverter.this.spELContext);
19641987
}
19651988

@@ -2000,21 +2023,21 @@ public <T> T getPropertyValue(MongoPersistentProperty property) {
20002023
*
20012024
* @author Oliver Gierke
20022025
*/
2003-
private static class ConverterAwareSpELExpressionParameterValueProvider
2004-
extends SpELExpressionParameterValueProvider<MongoPersistentProperty> {
2026+
private static class ConverterAwareValueExpressionParameterValueProvider
2027+
extends ValueExpressionParameterValueProvider<MongoPersistentProperty> {
20052028

20062029
private final ConversionContext context;
20072030

20082031
/**
2009-
* Creates a new {@link ConverterAwareSpELExpressionParameterValueProvider}.
2032+
* Creates a new {@link ConverterAwareValueExpressionParameterValueProvider}.
20102033
*
20112034
* @param context must not be {@literal null}.
20122035
* @param evaluator must not be {@literal null}.
20132036
* @param conversionService must not be {@literal null}.
20142037
* @param delegate must not be {@literal null}.
20152038
*/
2016-
public ConverterAwareSpELExpressionParameterValueProvider(ConversionContext context,
2017-
SpELExpressionEvaluator evaluator, ConversionService conversionService,
2039+
public ConverterAwareValueExpressionParameterValueProvider(ConversionContext context,
2040+
ValueExpressionEvaluator evaluator, ConversionService conversionService,
20182041
ParameterValueProvider<MongoPersistentProperty> delegate) {
20192042

20202043
super(evaluator, conversionService, delegate);
@@ -2025,7 +2048,7 @@ public ConverterAwareSpELExpressionParameterValueProvider(ConversionContext cont
20252048
}
20262049

20272050
@Override
2028-
protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, MongoPersistentProperty> parameter) {
2051+
protected <T> T potentiallyConvertExpressionValue(Object object, Parameter<T, MongoPersistentProperty> parameter) {
20292052
return context.convert(object, parameter.getType());
20302053
}
20312054
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.mongodb.core.convert;
1717

1818
import org.bson.conversions.Bson;
19+
1920
import org.springframework.data.convert.ValueConversionContext;
2021
import org.springframework.data.mapping.model.PropertyValueProvider;
2122
import org.springframework.data.mapping.model.SpELContext;

0 commit comments

Comments
 (0)