Spring Data Commons 的 Paged
接口提供值表达式支持,无需 Java lambdas 或额外框架,即可通过 EL、Spel 或 SpEL 轻松访问分页结果。该特性简化了复杂查询、排序和投影,使用起来简单、直观,即使在处理嵌套或复杂数据时也能始终如一地生成正确的 SQL 查询。
Value Expressions Fundamentals
值表达式是 {spring-framework-docs}/core/expressions.html[Spring 表达式语言 (SpEL)] 和 {spring-framework-docs}/core/beans/environment.html#beans-placeholder-resolution-in-statements[属性占位符解决] 的组合。它们将对程序表达式的强大评估与求助于属性占位符解决的简单性相结合,以从 Environment
获取值,如配置属性。
表达式应由受信任的输入(例如注释值)定义,而不是由用户输入确定。
以下代码演示如何在注释上下文中使用表达式。
.Annotation Usage
@Document("orders-#{tenantService.getOrderCollection()}-${tenant-config.suffix}")
class Order {
// …
}
值表达式可以从一个 SpEL 表达式、一个属性占位符或混合不同表达式的复合表达式(包括文本)定义。 .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 | 使用单个 SPEL 表达式的值表达式。 |
2 | 使用静态 SPEL 表达式评估为 2-hello-world 的值表达式。 |
3 | 使用单个属性占位符的值表达式。 |
4 | 复合表达式由字面量 orders- 和属性占位符 ${tenant-config.suffix} 组成。 |
5 | 使用 SpEL、属性占位符和文字的复合表达式。 |
使用值表达式为你的代码带来了很多灵活性。这样做需要在每次使用时计算表达式,因此值表达式计算会对性能状况产生影响。 |
Parsing and Evaluation
“ValueExpression” 是由“ValueExpressionParser” API 所解析。 ValueExpression
的实例是线程安全的,并且可缓存以供以后使用,从而避免重复解析。
以下示例显示了 Value Expression API 的用法:
-
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
,允许进行广泛的操作、访问静态类型和上下文扩展。
请始终解析和计算来自受信任来源(例如注释)的表达式。接受用户提供的表达式可能会创建进入点以利用应用程序上下文和系统,从而导致潜在的安全漏洞。 |
Extending the Evaluation Context
EvaluationContextProvider
及其响应变体 ReactiveEvaluationContextProvider
提供对 EvaluationContext
的访问。ExtensionAwareEvaluationContextProvider
及其响应变体 ReactiveExtensionAwareEvaluationContextProvider
是从应用程序上下文(特别是 ListableBeanFactory
)确定上下文扩展的默认实现。
扩展实现 EvaluationContextExtension
或 ReactiveEvaluationContextExtension
以提供扩展支持以填充 EvaluationContext
。这些扩展是根对象、属性和函数(顶级方法)。
以下示例展示了一个提供根对象、属性、函数和别名函数的上下文扩展。
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 表达式:
#{add(1, 2)} 1
#{extensionMethod()} 2
#{aliasedMethod()} 3
#{key} 4
#{rootObjectInstanceMethod()} 5
1 | 调用 add 由 MyExtension 声明的方法,方法将两个数字参数相加并返回其和,结果为 3 。 |
2 | 调用 add 由 MyExtension 声明的方法,方法将两个数字参数相加并返回其和,结果为 3 。 |
3 | 调用 add 方法。该方法公开为函数,并重定向到 add 由 MyExtension 声明的方法,结果为 3 。 |
4 | 计算 add 属性,结果为 MyExtension 。 |
5 | 对根对象实例 add 调用方法 MyExtension 。 |
你可以在 link:https://github.com/spring-projects/spring-security/blob/main/data/src/main/java/org/springframework/security/data/repository/query/SecurityEvaluationContextExtension.java[SecurityEvaluationContextExtension
中找到真实的上下文扩展。