mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 08:24:13 +00:00
Merge branch '7.0.x'
This commit is contained in:
@@ -286,6 +286,46 @@ registering property accessors, resolvers, variables, or functions – to ensure
|
||||
of the objects reachable via the context expose operations that would be dangerous if
|
||||
invoked by an expression from an untrusted source.
|
||||
|
||||
[[expressions-evaluation-context-lifecycle]]
|
||||
=== Lifecycle and Reuse
|
||||
|
||||
For performance, the AST nodes that make up a parsed `Expression` may cache the specific
|
||||
`PropertyAccessor`, `IndexAccessor`, `MethodExecutor`, or `ConstructorExecutor` that
|
||||
satisfied a previous evaluation, so that later evaluations of the same node can avoid
|
||||
asking every registered accessor or resolver in turn. Understanding this caching behavior
|
||||
is essential to using `Expression` and `EvaluationContext` correctly, in addition to the
|
||||
<<expressions-evaluation-context-security,security considerations>> discussed previously.
|
||||
|
||||
A parsed `Expression` is designed to be created once and evaluated repeatedly, and doing
|
||||
so is both supported and encouraged. In particular:
|
||||
|
||||
* A parsed `Expression` may be evaluated against different root objects, and against
|
||||
different `EvaluationContext` instances of the *same type and with equivalent
|
||||
configuration* – for example, several `StandardEvaluationContext` instances each
|
||||
registering the same kind of custom `PropertyAccessor`. Changing the accessors or
|
||||
resolvers registered with a context between evaluations of the same expression is
|
||||
atypical and generally not advised, but is expected to work correctly: the registered
|
||||
state of the *current* context is what is consulted, not a snapshot taken during an
|
||||
earlier evaluation.
|
||||
* A parsed `Expression` must *not* be evaluated first against a context with one set of
|
||||
security implications and later against a context with different, typically more
|
||||
restrictive, security implications – for example, first against a
|
||||
`StandardEvaluationContext` and later against a `SimpleEvaluationContext`. Doing so is
|
||||
analogous to executing a database query on behalf of an administrator, caching the
|
||||
resulting administrator-privileged execution plan, and then reusing that cached plan for
|
||||
a lower-privileged user while expecting the lower-privileged user's restrictions to
|
||||
apply: cached state from the first, more permissive evaluation may be reused during the
|
||||
second, and the second context's restrictions cannot be reliably enforced as a result.
|
||||
If the same expression string must be evaluated under contexts with different security
|
||||
implications, parse it into *distinct* `Expression` instances, one per context.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
Reusing a single parsed `Expression` across `EvaluationContext` instances with different
|
||||
security implications is not a supported usage pattern and must be avoided, regardless of
|
||||
which `EvaluationContext` implementations are involved.
|
||||
====
|
||||
|
||||
[[expressions-type-conversion]]
|
||||
=== Type Conversion
|
||||
|
||||
|
||||
+6
@@ -33,6 +33,12 @@ import org.jspecify.annotations.Nullable;
|
||||
* {@link AccessException} which signals to the infrastructure to go back to the
|
||||
* resolvers to ask for a new one.
|
||||
*
|
||||
* <p>A cached {@code ConstructorExecutor} is valid only for the
|
||||
* {@link EvaluationContext} configuration — in particular, the registered
|
||||
* {@link ConstructorResolver ConstructorResolvers} — under which it was resolved.
|
||||
* See {@link Expression} for the resulting contract on reusing a parsed expression
|
||||
* across different {@code EvaluationContext} instances.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
|
||||
@@ -56,6 +56,18 @@ import org.jspecify.annotations.Nullable;
|
||||
* those reachable objects expose operations that would be dangerous if invoked
|
||||
* by an expression from an untrusted source.
|
||||
*
|
||||
* <p>An {@code EvaluationContext} is designed to be built once and reused across
|
||||
* many evaluations, potentially of many different {@link Expression} instances. A
|
||||
* single {@link Expression}, in turn, may cache accessor and executor state resolved
|
||||
* against a particular {@code EvaluationContext} configuration. Reusing an
|
||||
* {@code Expression} across {@code EvaluationContext} instances of the same type and
|
||||
* with equivalent configuration is supported; reusing an {@code Expression} across
|
||||
* contexts with different security implications — for example, first against a
|
||||
* {@code StandardEvaluationContext} and later against a
|
||||
* {@code SimpleEvaluationContext} — is not, since cached state from the more
|
||||
* permissive evaluation may be reused during the more restrictive one. See
|
||||
* {@link Expression} for details.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
|
||||
@@ -27,6 +27,23 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||
*
|
||||
* <p>Provides a common abstraction for expression evaluation.
|
||||
*
|
||||
* <p>An {@code Expression} is intended to be parsed once and evaluated repeatedly,
|
||||
* potentially against different root objects and different {@link EvaluationContext}
|
||||
* instances. For performance, an {@code Expression} implementation may internally
|
||||
* cache the specific accessor or executor — for example, a
|
||||
* {@code PropertyAccessor}, {@code IndexAccessor}, {@code MethodExecutor}, or
|
||||
* {@code ConstructorExecutor} — that satisfied a previous evaluation, in order
|
||||
* to avoid repeatedly asking every candidate registered with the context. Reusing a
|
||||
* parsed {@code Expression} across {@code EvaluationContext} instances of the same
|
||||
* type and with equivalent configuration is supported; however, reusing a parsed
|
||||
* {@code Expression} across contexts with different security implications — for
|
||||
* example, first against a {@code StandardEvaluationContext} and later against a
|
||||
* {@code SimpleEvaluationContext} — is not supported, since cached state from a
|
||||
* more permissive evaluation may be reused during a subsequent, more restrictive
|
||||
* evaluation. If the same expression string must be evaluated under contexts with
|
||||
* different security implications, parse it into distinct {@code Expression}
|
||||
* instances, one per context.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
|
||||
@@ -30,6 +30,17 @@ import org.jspecify.annotations.Nullable;
|
||||
* {@linkplain #getSpecificTargetClasses() target classes} for which it should be
|
||||
* called. See {@link TargetedAccessor} for details.
|
||||
*
|
||||
* <p>SpEL infrastructure may cache an {@code IndexAccessor} instance on a per-node
|
||||
* basis once it has successfully served a read or write for a given index and target
|
||||
* type, in order to avoid repeatedly invoking {@link #canRead}/{@link #canWrite} on
|
||||
* every registered accessor for subsequent evaluations of that node. Before reusing a
|
||||
* cached instance, SpEL confirms that it is still registered with the current
|
||||
* {@link EvaluationContext}. Consequently, {@link #canRead}, {@link #canWrite},
|
||||
* {@link #read}, and {@link #write} should behave consistently for a given
|
||||
* {@code (context, target type, index)} combination for as long as the accessor remains
|
||||
* registered with a context, since a change in behavior may not be observed until the
|
||||
* accessor is re-selected from scratch.
|
||||
*
|
||||
* @author Jackmiking Lee
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
|
||||
@@ -32,6 +32,12 @@ import org.jspecify.annotations.Nullable;
|
||||
* {@link AccessException} which signals to the infrastructure to go back to the
|
||||
* resolvers to ask for a new one.
|
||||
*
|
||||
* <p>A cached {@code MethodExecutor} is valid only for the {@link EvaluationContext}
|
||||
* configuration — in particular, the registered {@link MethodResolver
|
||||
* MethodResolvers} — under which it was resolved. See {@link Expression} for the
|
||||
* resulting contract on reusing a parsed expression across different
|
||||
* {@code EvaluationContext} instances.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
|
||||
@@ -30,6 +30,17 @@ import org.jspecify.annotations.Nullable;
|
||||
* {@linkplain #getSpecificTargetClasses() target classes} for which it should be
|
||||
* called. See {@link TargetedAccessor} for details.
|
||||
*
|
||||
* <p>SpEL infrastructure may cache a {@code PropertyAccessor} instance on a per-node
|
||||
* basis once it has successfully served a read or write for a given property name and
|
||||
* target type, in order to avoid repeatedly invoking {@link #canRead}/{@link #canWrite}
|
||||
* on every registered accessor for subsequent evaluations of that node. Before reusing
|
||||
* a cached instance, SpEL confirms that it is still registered with the current
|
||||
* {@link EvaluationContext}. Consequently, {@link #canRead}, {@link #canWrite},
|
||||
* {@link #read}, and {@link #write} should behave consistently for a given
|
||||
* {@code (context, target type, name)} combination for as long as the accessor remains
|
||||
* registered with a context, since a change in behavior may not be observed until the
|
||||
* accessor is re-selected from scratch.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
* @see TargetedAccessor
|
||||
|
||||
+12
@@ -43,6 +43,18 @@ import org.springframework.util.Assert;
|
||||
* specified context. During expression evaluation the context may be asked to resolve
|
||||
* references to types, beans, properties, and methods.
|
||||
*
|
||||
* <p>The individual nodes of the parsed AST may cache the specific
|
||||
* {@link org.springframework.expression.PropertyAccessor PropertyAccessor},
|
||||
* {@link org.springframework.expression.IndexAccessor IndexAccessor},
|
||||
* {@link org.springframework.expression.MethodExecutor MethodExecutor}, or
|
||||
* {@link org.springframework.expression.ConstructorExecutor ConstructorExecutor} that
|
||||
* satisfied a previous evaluation of that node. On a subsequent evaluation, the current
|
||||
* {@code EvaluationContext} is consulted to confirm that the cached accessor or executor
|
||||
* (or, in some cases, the resolver that produced it) is still applicable before it is
|
||||
* reused; if it is not, resolution is performed again from scratch. See {@link Expression}
|
||||
* for the resulting contract on reusing a {@code SpelExpression} across different
|
||||
* {@code EvaluationContext} instances.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
|
||||
+10
@@ -105,6 +105,16 @@ import org.springframework.expression.spel.SpelMessage;
|
||||
* untrusted source. For details on what qualifies as a "trusted" source, see
|
||||
* {@link StandardEvaluationContext}.
|
||||
*
|
||||
* <p>Because a parsed {@code Expression} may cache accessor and executor state
|
||||
* resolved against a particular {@code EvaluationContext} configuration, a parsed
|
||||
* {@code Expression} must never be evaluated first against a
|
||||
* {@code StandardEvaluationContext} (or any other, less restrictive,
|
||||
* {@code EvaluationContext}) and later against a {@code SimpleEvaluationContext}. If the
|
||||
* same expression string must be evaluated under both kinds of contexts, parse it into
|
||||
* two distinct {@code Expression} instances instead. See
|
||||
* {@link org.springframework.expression.Expression Expression} for further details on
|
||||
* this lifecycle contract.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
|
||||
+9
@@ -80,6 +80,15 @@ import org.springframework.util.Assert;
|
||||
* regardless of the {@code EvaluationContext} implementation in use; see
|
||||
* {@link SimpleEvaluationContext} for further details.
|
||||
*
|
||||
* <p>Because a parsed {@code Expression} may cache accessor and executor state
|
||||
* resolved against a particular {@code EvaluationContext} configuration, a parsed
|
||||
* {@code Expression} must never be evaluated first against a
|
||||
* {@code StandardEvaluationContext} and later against a {@code SimpleEvaluationContext}
|
||||
* (or any other, more restrictive {@code EvaluationContext}). If the same expression
|
||||
* string must be evaluated under both kinds of contexts, parse it into two distinct
|
||||
* {@code Expression} instances instead. See {@link org.springframework.expression.Expression
|
||||
* Expression} for further details on this lifecycle contract.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
|
||||
Reference in New Issue
Block a user