Spring 表达式语言 (SpEL)
你可以使用用 Spring 表达式语言 编写的表达式来配置许多 Spring Integration 组件。
在大多数情况下,#root
对象是 Message
,它有两个属性(headers
和 payload
),这允许使用诸如 payload
、payload.thing
、headers['my.header']
等表达式。
在某些情况下,会提供额外的变量。例如,<int-http:inbound-gateway/>
提供了 #requestParams
(来自 HTTP 请求的参数)和 #pathVariables
(URI 中路径占位符的值)。
对于所有 SpEL 表达式,提供了一个 BeanResolver
以启用对应用程序上下文中任何 bean 的引用(例如,@myBean.foo(payload)
)。此外,还提供了两个 PropertyAccessors
。MapAccessor
可通过使用键来访问 Map
中的值,而 ReflectivePropertyAccessor
允许访问字段和 JavaBean 规范的属性(通过使用 getter 和 setter)。这就是你可以访问 Message
头和有效负载属性的方法。
SpEL 评估上下文自定义
从 Spring Integration 3.0 开始,您可以向框架使用的 SpEL 评估上下文中添加额外的 PropertyAccessor
实例。框架提供了(只读的)JsonPropertyAccessor
,您可以使用它来访问 JsonNode
或字符串中 JSON 的字段。您也可以根据具体需求创建自己的 PropertyAccessor
。
从 6.4 版本开始,提供了 JsonIndexAccessor
实现,该实现知道如何使用 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/>
被解析并添加到integrationEvaluationContext
的functions
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.JsonIndexAccessor"/>
</index-accessors>
<bean id="jsonPA" class="org.springframework.integration.json.JsonPropertyAccessor"/>
<ref bean="fooPropertyAccessor"/>
</int:spel-property-accessors>
在前面的例子中,两个自定义的 PropertyAccessor
实例按声明的顺序注入到 EvaluationContext
中。
要通过 Java 配置提供 PropertyAccessor
实例,你应该声明一个名为 spelPropertyAccessorRegistrar
的 SpelPropertyAccessorRegistrar
bean(由 IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME
常量规定)。以下示例展示了如何使用 Java 配置两个自定义的 PropertyAccessor
(从 6.4 版本开始包括 IndexAccessor
)实例:
@Bean
public SpelPropertyAccessorRegistrar spelPropertyAccessorRegistrar() {
return new SpelPropertyAccessorRegistrar(new JsonPropertyAccessor())
.add(fooPropertyAccessor())
.add(new JsonIndexAccessor());
}
在父上下文中声明的自定义 PropertyAccessor
实例也会在任何子上下文中可用。它们会被放置在结果列表的末尾(但在默认的 org.springframework.context.expression.MapAccessor
和 o.s.expression.spel.support.ReflectivePropertyAccessor
之前)。如果您在子上下文中声明了一个具有相同 bean ID 的 PropertyAccessor
,它将覆盖父级的访问器。在 <spel-property-accessors/>
中声明的 bean 必须具有 'id' 属性。最终的使用顺序如下:
-
当前上下文中的访问器,按照它们声明的顺序
-
父上下文中的任何访问器,按顺序
-
MapAccessor
-
ReflectivePropertyAccessor