Spring Data Commons 为 Spring Data 模块提供了通用基础结构,特别是用于创建实现分页和其他数据操作的自定义查询。这允许开发人员使用表达式语言直接在存储库查询方法中定义分页和其他操作,从而简化了查询的编写和执行。

Value Expressions Fundamentals

值表达式是 {spring-framework-docs}/core/expressions.html[Spring 表达式语言 (SpEL)] 和 {spring-framework-docs}/core/beans/environment.html#beans-placeholder-resolution-in-statements[属性占位符解决] 的组合。它们将对程序表达式的强大评估与求助于属性占位符解决的简单性相结合,以从 Environment 获取值,如配置属性。

Value Expressions are a combination of {spring-framework-docs}/core/expressions.html[Spring Expression Language (SpEL)] and {spring-framework-docs}/core/beans/environment.html#beans-placeholder-resolution-in-statements[Property Placeholder Resolution]. They combine powerful evaluation of programmatic expressions with the simplicity to resort to property-placeholder resolution to obtain values from the Environment such as configuration properties.

表达式应由受信任的输入(例如注释值)定义,而不是由用户输入确定。

Expressions are expected to be defined by a trusted input such as an annotation value and not to be determined from user input.

以下代码演示如何在注释上下文中使用表达式。

The following code demonstrates how to use expressions in the context of annotations. .Annotation Usage

@Document("orders-#{tenantService.getOrderCollection()}-${tenant-config.suffix}")
class Order {
  // …
}

值表达式可以从一个 SpEL 表达式、一个属性占位符或混合不同表达式的复合表达式(包括文本)定义。

Value Expressions can be defined from a sole SpEL Expression, a Property Placeholder or a composite expression mixing various expressions including literals. .Expression Examples

#{tenantService.getOrderCollection()}                          1
#{(1+1) + '-hello-world'}                                      2
${tenant-config.suffix}                                        3
orders-${tenant-config.suffix}                                 4
#{tenantService.getOrderCollection()}-${tenant-config.suffix}  5
1 Value Expression using a single SpEL Expression.
2 Value Expression using a static SpEL Expression evaluating to 2-hello-world.
3 Value Expression using a single Property Placeholder.
4 Composite expression comprised of the literal orders- and the Property Placeholder ${tenant-config.suffix}.
5 Composite expression using SpEL, Property Placeholders and literals.

使用值表达式为你的代码带来了很多灵活性。这样做需要在每次使用时计算表达式,因此值表达式计算会对性能状况产生影响。

Using value expressions introduces a lot of flexibility to your code. Doing so requires evaluation of the expression on each usage and, therefore, value expression evaluation has an impact on the performance profile.

Parsing and Evaluation

“ValueExpression” 是由“ValueExpressionParser” API 所解析。 ValueExpression 的实例是线程安全的,并且可缓存以供以后使用,从而避免重复解析。

Value Expressions are parsed by the ValueExpressionParser API. Instances of ValueExpression are thread-safe and can be cached for later use to avoid repeated parsing.

以下示例显示了 Value Expression API 的用法:

The following example shows the Value Expression API usage:

Parsing and Evaluation
  • Java

  • Kotlin

ValueParserConfiguration configuration = SpelExpressionParser::new;
ValueEvaluationContext context = ValueEvaluationContext.of(environment, evaluationContext);

ValueExpressionParser parser = ValueExpressionParser.create(configuration);
ValueExpression expression = parser.parse("Hello, World");
Object result = expression.evaluate(context);
val configuration = ValueParserConfiguration { SpelExpressionParser() }
val context = ValueEvaluationContext.of(environment, evaluationContext)

val parser = ValueExpressionParser.create(configuration)
val expression: ValueExpression = parser.parse("Hello, World")
val result: Any = expression.evaluate(context)

SpEL Expressions

{spring-framework-docs}/core/expressions.html[SpEL 表达式] 遵循模板样式,其中表达式应位于 #{…} 格式中。表达式使用由 EvaluationContextProvider 提供的 EvaluationContext 进行评估。上下文本身是一个强大的 StandardEvaluationContext,允许进行广泛的操作、访问静态类型和上下文扩展。

{spring-framework-docs}/core/expressions.html[SpEL Expressions] follow the Template style where the expression is expected to be enclosed within the #{…} format. Expressions are evaluated using an EvaluationContext that is provided by EvaluationContextProvider. The context itself is a powerful StandardEvaluationContext allowing a wide range of operations, access to static types and context extensions.

请始终解析和计算来自受信任来源(例如注释)的表达式。接受用户提供的表达式可能会创建进入点以利用应用程序上下文和系统,从而导致潜在的安全漏洞。

Make sure to parse and evaluate only expressions from trusted sources such as annotations. Accepting user-provided expressions can create an entry path to exploit the application context and your system resulting in a potential security vulnerability.

Extending the Evaluation Context

EvaluationContextProvider 及其响应变体 ReactiveEvaluationContextProvider 提供对 EvaluationContext 的访问。ExtensionAwareEvaluationContextProvider 及其响应变体 ReactiveExtensionAwareEvaluationContextProvider 是从应用程序上下文(特别是 ListableBeanFactory)确定上下文扩展的默认实现。

EvaluationContextProvider and its reactive variant ReactiveEvaluationContextProvider provide access to an EvaluationContext. ExtensionAwareEvaluationContextProvider and its reactive variant ReactiveExtensionAwareEvaluationContextProvider are default implementations that determine context extensions from an application context, specifically ListableBeanFactory.

扩展实现 EvaluationContextExtensionReactiveEvaluationContextExtension 以提供扩展支持以填充 EvaluationContext。这些扩展是根对象、属性和函数(顶级方法)。

Extensions implement either EvaluationContextExtension or ReactiveEvaluationContextExtension to provide extension support to hydrate EvaluationContext. That are a root object, properties and functions (top-level methods).

以下示例展示了一个提供根对象、属性、函数和别名函数的上下文扩展。

The following example shows a context extension that provides a root object, properties, functions and an aliased function.

Implementing a EvaluationContextExtension
  • Java

  • Kotlin

@Component
public class MyExtension implements EvaluationContextExtension {

    @Override
    public String getExtensionId() {
        return "my-extension";
    }

    @Override
    public Object getRootObject() {
        return new CustomExtensionRootObject();
    }

    @Override
    public Map<String, Object> getProperties() {

        Map<String, Object> properties = new HashMap<>();

        properties.put("key", "Hello");

        return properties;
    }

    @Override
    public Map<String, Function> getFunctions() {

        Map<String, Function> functions = new HashMap<>();

        try {
            functions.put("aliasedMethod", new Function(getClass().getMethod("extensionMethod")));
            return functions;
        } catch (Exception o_O) {
            throw new RuntimeException(o_O);
        }
    }

    public static String extensionMethod() {
        return "Hello World";
    }

    public static int add(int i1, int i2) {
        return i1 + i2;
    }

}

public class CustomExtensionRootObject {

	public boolean rootObjectInstanceMethod() {
		return true;
	}

}
@Component
class MyExtension : EvaluationContextExtension {

    override fun getExtensionId(): String {
        return "my-extension"
    }

    override fun getRootObject(): Any? {
        return CustomExtensionRootObject()
    }

    override fun getProperties(): Map<String, Any> {
        val properties: MutableMap<String, Any> = HashMap()

        properties["key"] = "Hello"

        return properties
    }

    override fun getFunctions(): Map<String, Function> {
        val functions: MutableMap<String, Function> = HashMap()

        try {
            functions["aliasedMethod"] = Function(javaClass.getMethod("extensionMethod"))
            return functions
        } catch (o_O: Exception) {
            throw RuntimeException(o_O)
        }
    }

    companion object {
        fun extensionMethod(): String {
            return "Hello World"
        }

        fun add(i1: Int, i2: Int): Int {
            return i1 + i2
        }
    }
}

class CustomExtensionRootObject {
	fun rootObjectInstanceMethod(): Boolean {
		return true
	}
}

在注册上述扩展后,你可以使用其导出的方法、属性和根对象来评估 SpEL 表达式:

Once the above shown extension is registered, you can use its exported methods, properties and root object to evaluate SpEL expressions:

Example 1. Expression Evaluation Examples
#{add(1, 2)}                                             1
#{extensionMethod()}                                     2
#{aliasedMethod()}                                       3
#{key}                                                   4
#{rootObjectInstanceMethod()}                            5
1 Invoke the method add declared by MyExtension resulting in 3 as the method adds both numeric parameters and returns the sum.
2 Invoke the method extensionMethod declared by MyExtension resulting in Hello World.
3 Invoke the method aliasedMethod. The method is exposed as function and redirects into the method extensionMethod declared by MyExtension resulting in Hello World.
4 Evaluate the key property resulting in Hello.
5 Invoke the method rootObjectInstanceMethod on the root object instance CustomExtensionRootObject.

你可以在 link:https://github.com/spring-projects/spring-security/blob/main/data/src/main/java/org/springframework/security/data/repository/query/SecurityEvaluationContextExtension.java[SecurityEvaluationContextExtension 中找到真实的上下文扩展。

You can find real-life context extensions at SecurityEvaluationContextExtension.

Property Placeholders

遵循 ${…} 形式的属性占位符引用通常由 Environment 通过 PropertySource 提供的属性。属性可用于解析系统属性、应用程序配置文件、环境配置或秘密管理系统提供的属性源。您可以在 {spring-framework-docs}/core/beans/annotation-config/value-annotations.html#page-title[Spring Framework 关于 @Value 用法的文档] 中找到有关属性占位符的更多详细信息。

Property placeholders following the form ${…} refer to properties provided typically by a PropertySource through Environment. Properties are useful to resolve against system properties, application configuration files, environment configuration or property sources contributed by secret management systems. You can find more details on the property placeholders in {spring-framework-docs}/core/beans/annotation-config/value-annotations.html#page-title[Spring Framework’s documentation on @Value usage].