值表达式是 Spring 表达式语言 (SpEL)属性占位符解析的组合。 它们将编程表达式的强大计算与诉诸属性占位符解析以从配置属性中获取值的简单性相结合。EnvironmentSpring中文文档

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

下面的代码演示如何在批注的上下文中使用表达式。Spring中文文档

例 1.注释用法
@Document("orders-#{tenantService.getOrderCollection()}-${tenant-config.suffix}")
class Order {
  // …
}

值表达式可以从单个 SpEL 表达式、属性占位符或混合各种表达式(包括文本)的复合表达式定义。Spring中文文档

例 2.表达式示例
#{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、属性占位符和文本的复合表达式。
使用值表达式可以为代码带来很大的灵活性。 这样做需要对每次使用情况的表达式进行评估,因此,值表达式评估会对性能配置文件产生影响。
1 使用单个 SpEL 表达式的值表达式。
2 使用静态 SpEL 表达式的值表达式,计算结果为 .2-hello-world
3 使用单个属性占位符的值表达式。
4 由文本和属性占位符组成的复合表达式。orders-${tenant-config.suffix}
5 使用 SpEL、属性占位符和文本的复合表达式。
使用值表达式可以为代码带来很大的灵活性。 这样做需要对每次使用情况的表达式进行评估,因此,值表达式评估会对性能配置文件产生影响。

解析和评估

值表达式由 API 解析。 的实例是线程安全的,可以缓存以供以后使用,以避免重复解析。ValueExpressionParserValueExpressionSpring中文文档

以下示例显示了 Value Expression API 用法:Spring中文文档

解析和评估
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 表达式遵循模板样式,其中表达式应包含在格式中。 表达式使用 提供的 来计算。 上下文本身功能强大,允许广泛的操作、访问静态类型和上下文扩展。#{…}EvaluationContextEvaluationContextProviderStandardEvaluationContextSpring中文文档

确保仅分析和计算来自受信任源(如批注)的表达式。 接受用户提供的表达式可能会创建条目路径,以利用应用程序上下文和系统,从而导致潜在的安全漏洞。

扩展评估上下文

EvaluationContextProvider及其反应式变体提供对 . 及其反应式变体是默认实现,用于从应用程序上下文确定上下文扩展,特别是 。ReactiveEvaluationContextProviderEvaluationContextExtensionAwareEvaluationContextProviderReactiveExtensionAwareEvaluationContextProviderListableBeanFactorySpring中文文档

扩展实现或提供扩展支持以水合物。 即根对象、属性和函数(顶级方法)。EvaluationContextExtensionReactiveEvaluationContextExtensionEvaluationContextSpring中文文档

以下示例显示了一个上下文扩展,该扩展提供根对象、属性、函数和别名函数。Spring中文文档

实现EvaluationContextExtension
@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 表达式:Spring中文文档

例 3.表达式计算示例
#{add(1, 2)}                                             (1)
#{extensionMethod()}                                     (2)
#{aliasedMethod()}                                       (3)
#{key}                                                   (4)
#{rootObjectInstanceMethod()}                            (5)
1 调用通过 result in 声明的方法,因为该方法将两个数值参数相加并返回总和。addMyExtension3
2 调用通过生成 .extensionMethodMyExtensionHello World
3 调用方法 。 该方法作为函数公开,并重定向到由 的结果 声明的方法。aliasedMethodextensionMethodMyExtensionHello World
4 计算得到 的属性。keyHello
5 在根对象实例上调用该方法。rootObjectInstanceMethodCustomExtensionRootObject

可以在 SecurityEvaluationContextExtension 中找到现实生活中的上下文扩展。Spring中文文档

确保仅分析和计算来自受信任源(如批注)的表达式。 接受用户提供的表达式可能会创建条目路径,以利用应用程序上下文和系统,从而导致潜在的安全漏洞。
1 调用通过 result in 声明的方法,因为该方法将两个数值参数相加并返回总和。addMyExtension3
2 调用通过生成 .extensionMethodMyExtensionHello World
3 调用方法 。 该方法作为函数公开,并重定向到由 的结果 声明的方法。aliasedMethodextensionMethodMyExtensionHello World
4 计算得到 的属性。keyHello
5 在根对象实例上调用该方法。rootObjectInstanceMethodCustomExtensionRootObject

属性占位符

表单后面的属性占位符是指通常由 到 提供的属性。 属性可用于根据机密管理系统提供的系统属性、应用程序配置文件、环境配置或属性源进行解析。 您可以在 Spring Framework 的 @Value 用法文档中找到有关属性占位符的更多详细信息。${…}PropertySourceEnvironmentSpring中文文档