diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java index 7a3328b6f..49cfee36c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java @@ -113,7 +113,7 @@ class AggregationUtils { SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser, - evaluationContextProvider.getEvaluationContext(method.getParameters(), accessor.getValues())); + () -> evaluationContextProvider.getEvaluationContext(method.getParameters(), accessor.getValues())); List target = new ArrayList<>(method.getAnnotatedAggregation().length); for (String source : method.getAnnotatedAggregation()) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java index c8ceac681..6547159b5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java @@ -73,7 +73,7 @@ class CollationUtils { if (StringUtils.trimLeadingWhitespace(collationExpression).startsWith("{")) { ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), - expressionParser, evaluationContextProvider.getEvaluationContext(parameters, accessor.getValues())); + expressionParser, () -> evaluationContextProvider.getEvaluationContext(parameters, accessor.getValues())); return Collation.from(CODEC.decode(collationExpression, bindingContext)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java index 5e1e73040..75c88961b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java @@ -117,7 +117,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { protected Query createQuery(ConvertingParameterAccessor accessor) { ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser, - evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues())); + () -> evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues())); Document queryObject = CODEC.decode(this.query, bindingContext); Document fieldsObject = CODEC.decode(this.fieldSpec, bindingContext); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java index 7b045e8f9..34b54f826 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java @@ -116,7 +116,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { protected Query createQuery(ConvertingParameterAccessor accessor) { ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser, - evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues())); + () -> evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues())); Document queryObject = CODEC.decode(this.query, bindingContext); Document fieldsObject = CODEC.decode(this.fieldSpec, bindingContext); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java index 012e67127..a5133cefd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java @@ -15,28 +15,54 @@ */ package org.springframework.data.mongodb.util.json; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import java.util.function.Supplier; +import org.springframework.data.util.Lazy; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; /** - * Reusable context for binding parameters to an placeholder or a SpEL expression within a JSON structure.
+ * Reusable context for binding parameters to a placeholder or a SpEL expression within a JSON structure.
* To be used along with {@link ParameterBindingDocumentCodec#decode(String, ParameterBindingContext)}. * * @author Christoph Strobl * @since 2.2 */ -@RequiredArgsConstructor -@Getter public class ParameterBindingContext { private final ValueProvider valueProvider; private final SpelExpressionParser expressionParser; - private final EvaluationContext evaluationContext; + private final Lazy evaluationContext; + + /** + * @param valueProvider + * @param expressionParser + * @param evaluationContext + * @deprecated since 2.2.3 - Please use + * {@link #ParameterBindingContext(ValueProvider, SpelExpressionParser, Supplier)} instead. + */ + @Deprecated + public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser, + EvaluationContext evaluationContext) { + + this(valueProvider, expressionParser, () -> evaluationContext); + } + + /** + * @param valueProvider + * @param expressionParser + * @param evaluationContext a {@link Supplier} for {@link Lazy} context retrieval. + * @since 2.2.3 + */ + public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser, + Supplier evaluationContext) { + + this.valueProvider = valueProvider; + this.expressionParser = expressionParser; + this.evaluationContext = evaluationContext instanceof Lazy ? (Lazy) evaluationContext : Lazy.of(evaluationContext); + } @Nullable public Object bindableValueForIndex(int index) { @@ -47,6 +73,18 @@ public class ParameterBindingContext { public Object evaluateExpression(String expressionString) { Expression expression = expressionParser.parseExpression(expressionString); - return expression.getValue(this.evaluationContext, Object.class); + return expression.getValue(getEvaluationContext(), Object.class); + } + + public EvaluationContext getEvaluationContext() { + return this.evaluationContext.get(); + } + + public SpelExpressionParser getExpressionParser() { + return expressionParser; + } + + public ValueProvider getValueProvider() { + return valueProvider; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java index 2ec51a79b..a70b65bbf 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java @@ -162,7 +162,7 @@ public class ParameterBindingDocumentCodec implements CollectibleCodec public Document decode(@Nullable String json, Object[] values) { return decode(json, new ParameterBindingContext((index) -> values[index], new SpelExpressionParser(), - EvaluationContextProvider.DEFAULT.getEvaluationContext(values))); + () -> EvaluationContextProvider.DEFAULT.getEvaluationContext(values))); } public Document decode(@Nullable String json, ParameterBindingContext bindingContext) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java index 1063414f1..406c42139 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java @@ -26,6 +26,7 @@ import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; +import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -96,6 +97,15 @@ public class ParameterBindingJsonReader extends AbstractBsonReader { public ParameterBindingJsonReader(String json, ValueProvider accessor, SpelExpressionParser spelExpressionParser, EvaluationContext evaluationContext) { + this(json, accessor, spelExpressionParser, () -> evaluationContext); + } + + /** + * @since 2.2.3 + */ + public ParameterBindingJsonReader(String json, ValueProvider accessor, SpelExpressionParser spelExpressionParser, + Supplier evaluationContext) { + this.scanner = new JsonScanner(json); setContext(new Context(null, BsonContextType.TOP_LEVEL)); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java index 66fd43fc8..50d0390b2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java @@ -26,6 +26,10 @@ import java.util.List; import org.bson.Document; import org.bson.codecs.DecoderContext; import org.junit.Test; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.TypedValue; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** * Unit tests for {@link ParameterBindingJsonReader}. @@ -197,6 +201,25 @@ public class ParameterBindingJsonReaderUnitTests { assertThat(target).isEqualTo(Document.parse("{ 'end_date' : { $gte : { $date : " + time + " } } } ")); } + @Test // DATAMONGO-2418 + public void shouldNotAccessSpElEveluationContextWhenNoSpelPresentInBindableTarget() { + + Object[] args = new Object[] { "value" }; + EvaluationContext evaluationContext = new StandardEvaluationContext() { + + @Override + public TypedValue getRootObject() { + throw new RuntimeException("o_O"); + } + }; + + ParameterBindingJsonReader reader = new ParameterBindingJsonReader("{ 'name':'?0' }", + new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); + Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); + + assertThat(target).isEqualTo(new Document("name", "value")); + } + private static Document parse(String json, Object... args) { ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args);