跳到主要内容
版本:7.0.2

Spring 表达式语言 (SpEL)

DeepSeek V3 中英对照 Spring Expression Language (SpEL)

您可以通过使用 Spring 表达式语言 编写的表达式来配置许多 Spring Integration 组件。

在大多数情况下,#root 对象是 Message,它有两个属性(headerspayload),允许使用诸如 payloadpayload.thingheaders['my.header'] 等表达式。

在某些情况下,会提供额外的变量。例如,<int-http:inbound-gateway/> 提供了 #requestParams(来自 HTTP 请求的参数)和 #pathVariables(来自 URI 中路径占位符的值)。

对于所有SpEL表达式,都可通过BeanResolver引用应用上下文中的任意Bean,例如@myBean.foo(payload)。此外,系统还提供两个PropertyAccessorMapAccessor支持通过键访问Map中的值,而ReflectivePropertyAccessor允许访问字段及符合JavaBean规范的属性(通过getter/setter方法)。通过这种方式,您可以访问Message的头部信息和负载属性。

SpEL 评估上下文自定义

从 Spring Integration 3.0 开始,你可以向框架使用的 SpEL 评估上下文添加额外的 PropertyAccessor 实例。框架提供了(只读的)JacksonPropertyAccessor,可用于访问 JsonNodeString 类型 JSON 中的字段。如果你有特定需求,也可以创建自己的 PropertyAccessor

提供了 JacksonIndexAccessor 实现,它知道如何使用 Jackson 的 ArrayNode API 从 JSON 数组中读取索引。支持以整数字面量形式提供的索引,例如 myJsonArray[1]。同时支持负索引,例如 myJsonArray[-1],它等同于 myJsonArray[myJsonArray.length - 1]。此外,对于任何越界的索引,将返回 null(详见 ArrayNode.get(int))。

此外,您还可以添加自定义函数。自定义函数是声明在类上的 static 方法。在框架中使用的任何 SpEL 表达式中,函数和属性访问器都是可用的。

以下配置展示了如何通过自定义属性访问器和函数直接配置 IntegrationEvaluationContextFactoryBean

<bean id="integrationEvaluationContext"
class="org.springframework.integration.config.IntegrationEvaluationContextFactoryBean">
<property name="propertyAccessors">
<util:map>
<entry key="things">
<bean class="things.MyCustomPropertyAccessor"/>
</entry>
</util:map>
</property>
<property name="functions">
<map>
<entry key="barcalc" value="#{T(things.MyFunctions).getMethod('calc', T(things.MyThing))}"/>
</map>
</property>
</bean>

从版本 6.4 开始,AbstractEvaluationContextFactoryBean 支持注入 IndexAccessor 实例。更多信息请参阅 AbstractEvaluationContextFactoryBean 方法的 Javadocs。

为了方便使用,Spring Integration 为属性访问器和函数提供了命名空间支持,具体内容将在后续章节中介绍。框架会自动为您配置工厂 bean。

该工厂bean定义覆盖了默认的integrationEvaluationContext bean定义。它在列表中添加了自定义访问器和一个自定义函数,该列表还包含前文提及的标准访问器。

请注意,自定义函数是静态方法。在前面的示例中,自定义函数是名为 MyFunctions 类上的一个静态方法,名为 calc,并接受一个类型为 MyThing 的单个参数。

假设你有一个 Message,其负载的类型为 MyThing。进一步假设,你需要执行某些操作,从 MyThing 创建一个名为 MyObject 的对象,然后在该对象上调用一个名为 calc 的自定义函数。

标准属性访问器不知道如何从 MyThing 中获取 MyObject,因此你可以编写并配置一个自定义属性访问器来实现此功能。最终,你的表达式可能会是 "#barcalc(payload.myObject)"

工厂 bean 还有另一个属性(typeLocator),它允许您自定义在 SpEL 求值期间使用的 TypeLocator。在某些使用非标准 ClassLoader 的环境中运行时,您可能需要这样做。在以下示例中,SpEL 表达式始终使用 bean 工厂的类加载器:

<bean id="integrationEvaluationContext"
class="org.springframework.integration.config.IntegrationEvaluationContextFactoryBean">
<property name="typeLocator">
<bean class="org.springframework.expression.spel.support.StandardTypeLocator">
<constructor-arg value="#{beanFactory.beanClassLoader}"/>
</bean>
</property>
</bean>

SpEL 函数

Spring Integration 提供了命名空间支持,允许您创建 SpEL 自定义函数。您可以指定 <spel-function/> 组件,以便为框架中使用的 EvaluationContext 提供自定义 SpEL 函数。您无需配置前面展示的工厂 Bean,只需添加一个或多个此类组件,框架便会自动将它们添加到默认的 integrationEvaluationContext 工厂 Bean 中。

例如,假设您有一个用于评估XPath的实用静态方法。以下示例展示了如何创建一个自定义函数来使用该方法:

<int:spel-function id="xpath"
class="com.something.test.XPathUtils" method="evaluate(java.lang.String, java.lang.Object)"/>

<int:transformer input-channel="in" output-channel="out"
expression="#xpath('//things/@mythings', payload)" />

根据前面的示例:

  • 默认的 IntegrationEvaluationContextFactoryBean bean(ID 为 integrationEvaluationContext)已注册到应用上下文中。

  • <spel-function/> 被解析并作为映射条目添加到 integrationEvaluationContextfunctions Map 中,其中其 id 作为键,静态 Method 作为值。

  • integrationEvaluationContext 工厂 bean 创建一个新的 StandardEvaluationContext 实例,并使用默认的 PropertyAccessor 实例、BeanResolver 和自定义函数进行配置。

  • EvaluationContext 实例被注入到 ExpressionEvaluatingTransformer bean 中。

要通过Java配置提供SpEL函数,你可以为每个函数声明一个SpelFunctionFactoryBean bean。以下示例展示了如何创建自定义函数:

@Bean
public SpelFunctionFactoryBean xpath() {
return new SpelFunctionFactoryBean(XPathUtils.class, "evaluate");
}
备注

在父上下文中声明的 SpEL 函数同样可以在任何子上下文中使用。每个上下文都有其自己的 integrationEvaluationContext 工厂 bean 实例,因为每个上下文都需要一个不同的 BeanResolver,但函数声明会被继承,并且可以通过声明一个同名的 SpEL 函数来覆盖。

内置 SpEL 函数

Spring Integration 提供了以下标准函数,这些函数在启动时会自动注册到应用程序上下文中:

  • #jsonPath: 在指定对象上执行 'jsonPath' 表达式求值。此函数调用 JsonPathUtils.evaluate(…​),该函数委托给 Jayway JsonPath 库。以下清单展示了一些使用示例:

    <transformer expression="#jsonPath(payload, '$.store.book[0].author')"/>

    <filter expression="#jsonPath(payload,'$..book[2].isbn') matches '\d-\d{3}-\d{5}-\d'"/>

    <splitter expression="#jsonPath(payload, '$.store.book')"/>

    <router expression="#jsonPath(payload, headers.jsonPath)">
    <mapping channel="output1" value="reference"/>
    <mapping channel="output2" value="fiction"/>
    </router>

    #jsonPath 还支持第三个(可选)参数:一个 com.jayway.jsonpath.Filter 数组,可以通过引用一个 bean 或 bean 方法等方式提供。

    备注

    使用此函数需要在类路径中包含 Jayway JsonPath 库 (json-path.jar)。否则,#jsonPath SpEL 函数将不会被注册。

    有关 JSON 的更多信息,请参阅 Transformer 中的“JSON 转换器”部分。

  • #xpath: 在提供的对象上执行 xpath 表达式求值。有关 XML 和 XPath 的更多信息,请参阅 XML 支持 - 处理 XML 负载

属性访问器

Spring Integration 提供了命名空间支持,允许您创建 SpEL 自定义 PropertyAccessor 实现。您可以使用 <spel-property-accessors/> 组件向框架中使用的 EvaluationContext 提供自定义 PropertyAccessor 实例列表。您无需配置之前展示的工厂 bean,只需添加此组件,框架便会自动将这些访问器添加到默认的 integrationEvaluationContext 工厂 bean 中。此外,从版本 6.4 开始,还提供了一个专用的 <index-accessors> 子元素,用于以类似方式配置 IndexAccessor bean。以下示例展示了如何操作:

<int:spel-property-accessors>
<index-accessors>
<beans:bean id="jsonIndex" class="org.springframework.integration.json.JacksonIndexAccessor"/>
</index-accessors>
<bean id="jsonPA" class="org.springframework.integration.json.JacksonPropertyAccessor"/>
<ref bean="fooPropertyAccessor"/>
</int:spel-property-accessors>

在前面的示例中,两个自定义的 PropertyAccessor 实例被注入到 EvaluationContext 中(按照它们声明的顺序)。

要通过Java配置提供PropertyAccessor实例,您应声明一个名为spelPropertyAccessorRegistrarSpelPropertyAccessorRegistrar bean(该名称由IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME常量规定)。以下示例展示了如何使用Java配置两个自定义PropertyAccessor实例(以及从版本6.4开始支持的IndexAccessor实例):

@Bean
public SpelPropertyAccessorRegistrar spelPropertyAccessorRegistrar() {
return new SpelPropertyAccessorRegistrar(new JacksonPropertyAccessor())
.add(fooPropertyAccessor())
.add(new JacksonIndexAccessor());
}
备注

在父上下文中声明的自定义 PropertyAccessor 实例也会在任何子上下文中可用。它们被放置在结果列表的末尾(但在默认的 org.springframework.context.expression.MapAccessoro.s.expression.spel.support.ReflectivePropertyAccessor 之前)。如果你在子上下文中声明了一个具有相同 bean ID 的 PropertyAccessor,它将覆盖父级的访问器。在 <spel-property-accessors/> 内声明的 bean 必须具有 'id' 属性。最终的使用顺序如下:

  • 当前上下文中的访问器,按照它们声明的顺序

  • 来自父上下文的任何访问器,按顺序

  • MapAccessor

  • ReflectivePropertyAccessor