Spring 表达式语言 (SpEL)
可以使用 Spring 表达式语言 编写的表达式来配置许多 Spring 集成 组件。
在大多数情况下,#root
对象是 Message
,它有两个属性(headers
和 payload
),允许使用诸如 payload
、payload.thing
、headers['my.header']
等表达式。
在某些情况下,还提供了额外的变量。例如,<int-http:inbound-gateway/>
提供 #requestParams
(来自 HTTP 请求的参数)和 #pathVariables
(来自 URI 中路径占位符的值)。
对于所有 SpEL 表达式,都可以使用 BeanResolver
来引用应用上下文中的任何 bean(例如,@myBean.foo(payload)
)。此外,还提供了两个 PropertyAccessor
。MapAccessor
允许使用 key 访问 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)
)。
此外,你还可以添加自定义函数。自定义函数是类中声明的静态方法。在整个框架中使用的任何 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
方法的 JavaDoc。
为了方便起见,Spring Integration 为属性访问器和函数提供了命名空间支持,如下一节所述。框架会自动为你配置 factory bean。
这个 factory bean 定义会覆盖默认的 integrationEvaluationContext
bean 定义。它将自定义访问器和一个自定义函数添加到列表中(该列表也包含前面提到的标准访问器)。
请注意,自定义函数是静态方法。在前面的例子中,自定义函数是 MyFunctions
类中名为 calc
的静态方法,它接受一个 MyThing
类型的参数。
假设你有一个 Message
,其载荷类型为 MyThing
。进一步假设你需要执行一些操作从 MyThing
创建一个名为 MyObject
的对象,然后在该对象上调用名为 calc
的自定义函数。
标准属性访问器不知道如何从 MyThing
获取 MyObject
,因此你可以编写并配置一个自定义属性访问器来实现。这样,你的最终表达式可能就是 "#barcalc(payload.myObject)"
。
该 factory bean 还有另一个属性(typeLocator
),允许你定制 SpEL 求值期间使用的 TypeLocator
。在某些使用非标准 ClassLoader
的环境中运行时,你可能需要这样做。在以下示例中,SpEL 表达式总是使用 bean factory 的类加载器
<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 函数。你可以添加一个或多个此类组件,而不是像前面那样配置 factory bean,框架会自动将它们添加到默认的 integrationEvaluationContext
factory 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)" />
鉴于前面的例子
-
ID 为
integrationEvaluationContext
的默认IntegrationEvaluationContextFactoryBean
bean 会注册到应用上下文中。 -
<spel-function/>
会被解析,并作为 map entry 添加到integrationEvaluationContext
的functions
Map 中,其id
作为 key,静态Method
作为 value。 -
integrationEvaluationContext
factory bean 创建一个新的StandardEvaluationContext
实例,并配置了默认的PropertyAccessor
实例、一个BeanResolver
和自定义函数。 -
该
EvaluationContext
实例会被注入到ExpressionEvaluatingTransformer
bean 中。
要使用 Java 配置提供 SpEL 函数,你可以为每个函数声明一个 SpelFunctionFactoryBean
bean。以下示例展示了如何创建一个自定义函数
@Bean
public SpelFunctionFactoryBean xpath() {
return new SpelFunctionFactoryBean(XPathUtils.class, "evaluate");
}
在父上下文中声明的 SpEL 函数在任何子上下文中也可用。每个上下文都有自己的 integrationEvaluationContext factory 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 方法(例如)来提供。使用此函数需要 classpath 中包含 Jayway JsonPath 库( json-path.jar
)。否则,#jsonPath
SpEL 函数将不会注册。有关 JSON 的更多信息,请参阅 转换器 中的 'JSON 转换器'。
-
#xpath
:在提供的对象上求值 xpath。有关 XML 和 XPath 的更多信息,请参阅 XML 支持 - 处理 XML 载荷。
属性访问器
Spring Integration 提供了命名空间支持,允许你创建 SpEL 自定义 PropertyAccessor
实现。你可以使用 <spel-property-accessors/>
组件,为整个框架中使用的 EvaluationContext
提供自定义 PropertyAccessor
实例列表。你可以添加此组件,而不是像前面那样配置 factory bean,框架会自动将访问器添加到默认的 integrationEvaluationContext
factory 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());
}
在父上下文中声明的自定义
|