Browse Source

Disable variable assignment in SimpleEvaluationContext

This commit introduces infrastructure to differentiate between
programmatic setting of a variable in an EvaluationContext versus the
assignment of a variable within a SpEL expression using the assignment
operator (=). In addition, this commit disables variable assignment
within expressions when using the SimpleEvaluationContext.

Closes gh-30328
pull/30400/head
Sam Brannen 3 years ago committed by Brian Clozel
parent
commit
e246b47f4d
  1. 45
      spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java
  2. 33
      spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java
  3. 6
      spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java
  4. 7
      spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java
  5. 13
      spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java
  6. 14
      spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java
  7. 12
      spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java
  8. 32
      spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java
  9. 21
      spring-expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java
  10. 15
      spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java
  11. 24
      spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java

45
spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 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.
@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
package org.springframework.expression;
import java.util.List;
import java.util.function.Supplier;
import org.springframework.lang.Nullable;
@ -24,12 +25,21 @@ import org.springframework.lang.Nullable; @@ -24,12 +25,21 @@ import org.springframework.lang.Nullable;
* Expressions are executed in an evaluation context. It is in this context that
* references are resolved when encountered during expression evaluation.
*
* <p>There is a default implementation of this EvaluationContext interface:
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}
* which can be extended, rather than having to implement everything manually.
* <p>There are two default implementations of this interface.
* <ul>
* <li>{@link org.springframework.expression.spel.support.SimpleEvaluationContext
* SimpleEvaluationContext}: a simpler builder-style {@code EvaluationContext}
* variant for data-binding purposes, which allows for opting into several SpEL
* features as needed.</li>
* <li>{@link org.springframework.expression.spel.support.StandardEvaluationContext
* StandardEvaluationContext}: a powerful and highly configurable {@code EvaluationContext}
* implementation, which can be extended, rather than having to implement everything
* manually.</li>
* </ul>
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public interface EvaluationContext {
@ -85,7 +95,30 @@ public interface EvaluationContext { @@ -85,7 +95,30 @@ public interface EvaluationContext {
OperatorOverloader getOperatorOverloader();
/**
* Set a named variable within this evaluation context to a specified value.
* Assign the value created by the specified {@link Supplier} to a named variable
* within this evaluation context.
* <p>In contrast to {@link #setVariable(String, Object)}, this method should only
* be invoked to support the assignment operator ({@code =}) within an expression.
* <p>By default, this method delegates to {@code setVariable(String, Object)},
* providing the value created by the {@code valueSupplier}. Concrete implementations
* may override this <em>default</em> method to provide different semantics.
* @param name the name of the variable to assign
* @param valueSupplier the supplier of the value to be assigned to the variable
* @return a {@link TypedValue} wrapping the assigned value
* @since 5.2.24
*/
default TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier) {
TypedValue typedValue = valueSupplier.get();
setVariable(name, typedValue.getValue());
return typedValue;
}
/**
* Set a named variable in this evaluation context to a specified value.
* <p>In contrast to {@link #assignVariable(String, Supplier)}, this method
* should only be invoked programmatically when interacting directly with the
* {@code EvaluationContext} &mdash; for example, to provide initial
* configuration for the context.
* @param name the name of the variable to set
* @param value the value to be placed in the variable
*/
@ -93,7 +126,7 @@ public interface EvaluationContext { @@ -93,7 +126,7 @@ public interface EvaluationContext {
/**
* Look up a named variable within this evaluation context.
* @param name variable to lookup
* @param name the name of the variable to look up
* @return the value of the variable, or {@code null} if not found
*/
@Nullable

33
spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 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.
@ -23,6 +23,7 @@ import java.util.HashMap; @@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.Supplier;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
@ -38,18 +39,19 @@ import org.springframework.util.Assert; @@ -38,18 +39,19 @@ import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to
* it are not seen by other expressions but it gives a place to hold local variables and
* ExpressionState is for maintaining per-expression-evaluation state: any changes to
* it are not seen by other expressions, but it gives a place to hold local variables and
* for component expressions in a compound expression to communicate state. This is in
* contrast to the EvaluationContext, which is shared amongst expression evaluations, and
* any changes to it will be seen by other expressions or any code that chooses to ask
* questions of the context.
*
* <p>It also acts as a place for to define common utility routines that the various AST
* <p>It also acts as a place to define common utility routines that the various AST
* nodes might need.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class ExpressionState {
@ -138,6 +140,29 @@ public class ExpressionState { @@ -138,6 +140,29 @@ public class ExpressionState {
return this.scopeRootObjects.element();
}
/**
* Assign the value created by the specified {@link Supplier} to a named variable
* within the evaluation context.
* <p>In contrast to {@link #setVariable(String, Object)}, this method should
* only be invoked to support assignment within an expression.
* @param name the name of the variable to assign
* @param valueSupplier the supplier of the value to be assigned to the variable
* @return a {@link TypedValue} wrapping the assigned value
* @since 5.2.24
* @see EvaluationContext#assignVariable(String, Supplier)
*/
public TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier) {
return this.relatedContext.assignVariable(name, valueSupplier);
}
/**
* Set a named variable in the evaluation context to a specified value.
* <p>In contrast to {@link #assignVariable(String, Supplier)}, this method
* should only be invoked programmatically.
* @param name the name of the variable to set
* @param value the value to be placed in the variable
* @see EvaluationContext#setVariable(String, Object)
*/
public void setVariable(String name, @Nullable Object value) {
this.relatedContext.setVariable(name, value);
}

6
spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java

@ -276,7 +276,11 @@ public enum SpelMessage { @@ -276,7 +276,11 @@ public enum SpelMessage {
/** @since 5.2.24 */
MAX_EXPRESSION_LENGTH_EXCEEDED(Kind.ERROR, 1079,
"SpEL expression is too long, exceeding the threshold of ''{0}'' characters");
"SpEL expression is too long, exceeding the threshold of ''{0}'' characters"),
/** @since 5.2.24 */
VARIABLE_ASSIGNMENT_NOT_SUPPORTED(Kind.ERROR, 1080,
"Assignment to variable ''{0}'' is not supported");
private final Kind kind;

7
spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@ -27,6 +27,7 @@ import org.springframework.expression.spel.ExpressionState; @@ -27,6 +27,7 @@ import org.springframework.expression.spel.ExpressionState;
* <p>Example: 'someNumberProperty=42'
*
* @author Andy Clement
* @author Sam Brannen
* @since 3.0
*/
public class Assign extends SpelNodeImpl {
@ -38,9 +39,7 @@ public class Assign extends SpelNodeImpl { @@ -38,9 +39,7 @@ public class Assign extends SpelNodeImpl {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue newValue = this.children[1].getValueInternal(state);
getChild(0).setValue(state, newValue.getValue());
return newValue;
return this.children[0].setValueInternal(state, () -> this.children[1].getValueInternal(state));
}
@Override

13
spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.util.StringJoiner;
import java.util.function.Supplier;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException;
@ -24,13 +25,13 @@ import org.springframework.expression.TypedValue; @@ -24,13 +25,13 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.lang.Nullable;
/**
* Represents a DOT separated expression sequence, such as
* {@code 'property1.property2.methodOne()'}.
*
* @author Andy Clement
* @author Sam Brannen
* @since 3.0
*/
public class CompoundExpression extends SpelNodeImpl {
@ -95,8 +96,12 @@ public class CompoundExpression extends SpelNodeImpl { @@ -95,8 +96,12 @@ public class CompoundExpression extends SpelNodeImpl {
}
@Override
public void setValue(ExpressionState state, @Nullable Object value) throws EvaluationException {
getValueRef(state).setValue(value);
public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
throws EvaluationException {
TypedValue typedValue = valueSupplier.get();
getValueRef(state).setValue(typedValue.getValue());
return typedValue;
}
@Override

14
spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@ -25,6 +25,7 @@ import java.util.Collection; @@ -25,6 +25,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.function.Supplier;
import org.springframework.asm.MethodVisitor;
import org.springframework.core.convert.TypeDescriptor;
@ -45,11 +46,12 @@ import org.springframework.util.ReflectionUtils; @@ -45,11 +46,12 @@ import org.springframework.util.ReflectionUtils;
/**
* An Indexer can index into some proceeding structure to access a particular piece of it.
* Supported structures are: strings / collections (lists/sets) / arrays.
* <p>Supported structures are: strings / collections (lists/sets) / arrays.
*
* @author Andy Clement
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sam Brannen
* @since 3.0
*/
// TODO support multidimensional arrays
@ -102,8 +104,12 @@ public class Indexer extends SpelNodeImpl { @@ -102,8 +104,12 @@ public class Indexer extends SpelNodeImpl {
}
@Override
public void setValue(ExpressionState state, @Nullable Object newValue) throws EvaluationException {
getValueRef(state).setValue(newValue);
public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
throws EvaluationException {
TypedValue typedValue = valueSupplier.get();
getValueRef(state).setValue(typedValue.getValue());
return typedValue;
}
@Override

12
spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@ -21,6 +21,7 @@ import java.util.ArrayList; @@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor;
@ -46,6 +47,7 @@ import org.springframework.util.ReflectionUtils; @@ -46,6 +47,7 @@ import org.springframework.util.ReflectionUtils;
* @author Andy Clement
* @author Juergen Hoeller
* @author Clark Duplichien
* @author Sam Brannen
* @since 3.0
*/
public class PropertyOrFieldReference extends SpelNodeImpl {
@ -147,8 +149,12 @@ public class PropertyOrFieldReference extends SpelNodeImpl { @@ -147,8 +149,12 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
@Override
public void setValue(ExpressionState state, @Nullable Object newValue) throws EvaluationException {
writeProperty(state.getActiveContextObject(), state.getEvaluationContext(), this.name, newValue);
public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
throws EvaluationException {
TypedValue typedValue = valueSupplier.get();
writeProperty(state.getActiveContextObject(), state.getEvaluationContext(), this.name, typedValue.getValue());
return typedValue;
}
@Override

32
spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast; @@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.function.Supplier;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
@ -40,6 +41,7 @@ import org.springframework.util.ObjectUtils; @@ -40,6 +41,7 @@ import org.springframework.util.ObjectUtils;
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public abstract class SpelNodeImpl implements SpelNode, Opcodes {
@ -64,7 +66,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes { @@ -64,7 +66,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
* <p>The descriptor is like the bytecode form but is slightly easier to work with.
* It does not include the trailing semicolon (for non array reference types).
* Some examples: Ljava/lang/String, I, [I
*/
*/
@Nullable
protected volatile String exitTypeDescriptor;
@ -83,8 +85,8 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes { @@ -83,8 +85,8 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
/**
* Return {@code true} if the next child is one of the specified classes.
*/
* Return {@code true} if the next child is one of the specified classes.
*/
protected boolean nextChildIs(Class<?>... classes) {
if (this.parent != null) {
SpelNodeImpl[] peers = this.parent.children;
@ -125,6 +127,28 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes { @@ -125,6 +127,28 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
@Override
public void setValue(ExpressionState expressionState, @Nullable Object newValue) throws EvaluationException {
setValueInternal(expressionState, () -> new TypedValue(newValue));
}
/**
* Evaluate the expression to a node and then set the new value created by the
* specified {@link Supplier} on that node.
* <p>For example, if the expression evaluates to a property reference, then the
* property will be set to the new value.
* <p>Favor this method over {@link #setValue(ExpressionState, Object)} when
* the value should be lazily computed.
* <p>By default, this method throws a {@link SpelEvaluationException},
* effectively disabling this feature. Subclasses may override this method to
* provide an actual implementation.
* @param expressionState the current expression state (includes the context)
* @param valueSupplier a supplier of the new value
* @throws EvaluationException if any problem occurs evaluating the expression or
* setting the new value
* @since 5.2.24
*/
public TypedValue setValueInternal(ExpressionState expressionState, Supplier<TypedValue> valueSupplier)
throws EvaluationException {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
}

21
spring-expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@ -17,9 +17,11 @@ @@ -17,9 +17,11 @@
package org.springframework.expression.spel.ast;
import java.lang.reflect.Modifier;
import java.util.function.Supplier;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
@ -27,10 +29,11 @@ import org.springframework.expression.spel.SpelEvaluationException; @@ -27,10 +29,11 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.lang.Nullable;
/**
* Represents a variable reference, eg. #someVar. Note this is different to a *local*
* variable like $someVar
* Represents a variable reference &mdash; for example, {@code #someVar}. Note
* that this is different than a <em>local</em> variable like {@code $someVar}.
*
* @author Andy Clement
* @author Sam Brannen
* @since 3.0
*/
public class VariableReference extends SpelNodeImpl {
@ -53,14 +56,14 @@ public class VariableReference extends SpelNodeImpl { @@ -53,14 +56,14 @@ public class VariableReference extends SpelNodeImpl {
@Override
public ValueRef getValueRef(ExpressionState state) throws SpelEvaluationException {
if (this.name.equals(THIS)) {
return new ValueRef.TypedValueHolderValueRef(state.getActiveContextObject(),this);
return new ValueRef.TypedValueHolderValueRef(state.getActiveContextObject(), this);
}
if (this.name.equals(ROOT)) {
return new ValueRef.TypedValueHolderValueRef(state.getRootContextObject(),this);
return new ValueRef.TypedValueHolderValueRef(state.getRootContextObject(), this);
}
TypedValue result = state.lookupVariable(this.name);
// a null value will mean either the value was null or the variable was not found
return new VariableRef(this.name,result,state.getEvaluationContext());
return new VariableRef(this.name, result, state.getEvaluationContext());
}
@Override
@ -90,8 +93,10 @@ public class VariableReference extends SpelNodeImpl { @@ -90,8 +93,10 @@ public class VariableReference extends SpelNodeImpl {
}
@Override
public void setValue(ExpressionState state, @Nullable Object value) throws SpelEvaluationException {
state.setVariable(this.name, value);
public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
throws EvaluationException {
return state.assignVariable(this.name, valueSupplier);
}
@Override

15
spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 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.
@ -21,6 +21,7 @@ import java.util.Collections; @@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
@ -78,6 +79,7 @@ import org.springframework.lang.Nullable; @@ -78,6 +79,7 @@ import org.springframework.lang.Nullable;
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Sam Brannen
* @since 4.3.15
* @see #forPropertyAccessors
* @see #forReadOnlyDataBinding()
@ -200,6 +202,17 @@ public final class SimpleEvaluationContext implements EvaluationContext { @@ -200,6 +202,17 @@ public final class SimpleEvaluationContext implements EvaluationContext {
return this.operatorOverloader;
}
/**
* {@code SimpleEvaluationContext} does not support variable assignment within
* expressions.
* @throws SpelEvaluationException with {@link SpelMessage#VARIABLE_ASSIGNMENT_NOT_SUPPORTED}
* @since 5.2.24
*/
@Override
public TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier) {
throw new SpelEvaluationException(SpelMessage.VARIABLE_ASSIGNMENT_NOT_SUPPORTED, "#" + name);
}
@Override
public void setVariable(String name, @Nullable Object value) {
this.variables.put(name, value);

24
spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java

@ -24,6 +24,8 @@ import java.util.List; @@ -24,6 +24,8 @@ import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
@ -38,6 +40,7 @@ import org.springframework.expression.MethodResolver; @@ -38,6 +40,7 @@ import org.springframework.expression.MethodResolver;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeLocator;
import org.springframework.expression.spel.testresources.TestPerson;
@ -367,9 +370,26 @@ public class EvaluationTests extends AbstractExpressionTests { @@ -367,9 +370,26 @@ public class EvaluationTests extends AbstractExpressionTests {
}
// assignment
@Test
public void testAssignmentToVariables01() {
evaluate("#var1='value1'", "value1", String.class);
void assignmentToVariableWithStandardEvaluationContext() {
evaluate("#var1 = 'value1'", "value1", String.class);
}
@ParameterizedTest
@CsvSource(delimiterString = "->", value = {
"'#var1 = \"value1\"' -> #var1",
"'true ? #myVar = 4 : 0' -> #myVar"
})
void assignmentToVariableWithSimpleEvaluationContext(String expression, String varName) {
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
Expression expr = parser.parseExpression(expression);
assertThatExceptionOfType(SpelEvaluationException.class)
.isThrownBy(() -> expr.getValue(context))
.satisfies(ex -> {
assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.VARIABLE_ASSIGNMENT_NOT_SUPPORTED);
assertThat(ex.getInserts()).as("inserts").containsExactly(varName);
});
}
@Test

Loading…
Cancel
Save