价值表达式基础
值表达式是 Spring 表达式语言(SpEL)和属性占位符解析的结合。
它们结合了对程序表达式的强大评估,同时便于诉诸属性占位符解析以从中获得环境例如配置属性。
表达式应由可信输入定义,如注释值,而非由用户输入确定。
范围
值表达式在不同注释的上下文中使用。 Spring Data 在两个主要情境下提供价值表达式评估:
-
映射模型注释:例如
@Document,@Field,@Value以及 Spring Data 模块中自带的映射模型(如 MongoDB、Elasticsearch、Cassandra、Neo4j)中的其他注释。 基于提供自身映射模型的库(如JPA、LDAP)构建的模块,在映射注释中不支持值表达式。以下代码演示了如何在映射模型注释的语境中使用表达式。
例子1。@Document注释的使用@Document("orders-#{tenantService.getOrderCollection()}-${tenant-config.suffix}") class Order { // … } -
存储库查询方法:主要通过
@Query.以下代码演示了如何在仓库查询方法的上下文中使用表达式。
例子2。@Query注释的使用class OrderRepository extends Repository<Order, String> { @Query("select u from User u where u.tenant = ?${spring.application.name:unknown} and u.firstname like %?#{escape([0])}% escape ?#{escapeCharacter()}") List<Order> findContainingEscaped(String namePart); }
查阅你模块的文档,确定参数的实际按名称/索引绑定语法。
通常,表达式前缀为:#{…}/:${…}或?#{…}/?${…}. |
表达式语法
值表达式可以由单独的 SpEL 表达式、属性占位符或混合多种表达式(包括文字)来定义。
#{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-你好-世界. |
| 3 | 使用单一属性占位符进行价值表达。 |
| 4 | 复合表达式由字面订单-以及属性占位符${tenant-config.suffix}. |
| 5 | 使用SpEL、属性占位符和文字的复合表达式。 |
| 使用值表达式为代码带来了很大的灵活性。 这样做需要对每次使用时的表达式进行评估,因此价值表达式的评估会影响性能配置。 |
Spring表达式语言(SpEL)和属性占位符解析详细解释了SpEL和属性占位符的语法和功能。
解析与评估
价值表达式通过ValueExpressionParser应用程序接口。
实例价值表达线程安全,可以缓存以备后用,以避免重复解析。
以下示例展示了 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表达式
SpEL表达式遵循模板样式,表达式应被包含在#{…}格式。
表达式的计算方式是评价背景由 提供评估ContextProvider.
这个背景本身就是一个强大的标准评估上下文允许多种作,访问静态类型和上下文扩展。
| 确保只解析和评估来自可信来源的表达式,如注释。 接受用户提供的表达式可能会创建一条进入路径,利用应用上下文和系统,从而引发潜在的安全漏洞。 |
扩展评估背景
评估ContextProvider及其响应式变体ReactiveEvaluationContextProvider提供访问评价背景.ExtensionAwareEvaluationContextProvider及其响应式变体ReactiveExtensionAwareEvaluationContextProvider是默认实现,用于确定应用上下文扩展,具体来说ListableBeanFactory.
扩展实现了以下评估上下文扩展或反应评估上下文扩展提供延长支持以补水评价背景.
它们包括根对象、属性和函数(顶层方法)。
以下示例展示了一个上下文扩展,提供了根对象、属性、函数和一个别名函数。
评估上下文扩展-
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 | 调用该方法加由我的扩展结果为3因为该方法同时加了数值参数并返回求和。 |
| 2 | 调用该方法extensionMethod由我的扩展结果为世界您好. |
| 3 | 调用该方法aliasedMethod.
该方法以函数形式暴露并重定向到该方法中extensionMethod由我的扩展结果为世界您好. |
| 4 | 评估钥匙性质,结果为你好. |
| 5 | 调用该方法rootObjectInstanceMethod在根对象实例上CustomExtensionRootObject. |
你可以在安全评估上下文扩展.
属性占位符
以下形式的属性占位符${…}参考通常由地产来源通过环境.
属性对于针对系统属性、应用配置文件、环境配置或由秘密管理系统贡献的属性源进行解析非常有用。
你可以在以下内容中找到更多关于属性占位符的细节Spring Framework 关于@Value用法.