# Spring 表达式语言

# Spring 表达式语言

你可以通过使用Spring Expression Language (opens new window)中编写的表达式来配置许多 Spring 集成组件。

在大多数情况下,#root对象是Message,它有两个属性(headerspayload),允许这样的表达式,如payloadpayload.thingheaders['my.header'],以此类推。

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

对于所有 SPEL 表达式,可以使用BeanResolver来启用对应用程序上下文中的任何 Bean 的引用(例如,@myBean.foo(payload))。此外,还有两个PropertyAccessors可用。MapAccessor允许通过使用一个键和ReflectivePropertyAccessor访问Map中的值,这允许访问字段和 JavaBean 兼容的属性(通过使用 getter 和 setter)。这就是访问Message标头和有效负载属性的方法。

# spel 评估上下文定制

从 Spring Integration3.0 开始,你可以向框架使用的 SPEL 评估上下文添加额外的PropertyAccessor实例。该框架提供(只读)JsonPropertyAccessor,你可以使用它从JsonNodeString中的 JSON 访问字段。如果你有特定的需求,也可以创建自己的PropertyAccessor

此外,你还可以添加自定义功能。自定义函数是在类上声明的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>

Spring 为了方便起见,集成为属性访问器和函数提供了名称空间支持,如下文所述。该框架自动为你配置工厂 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 集成提供了名称空间支持,以允许你创建 SPEL 自定义函数。你可以指定<spel-function/>组件来为整个框架中使用的自定义 SPEL 函数 (opens new window)提供EvaluationContext。你可以添加一个或多个这些组件,而不是配置前面所示的工厂 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)" />

给出了前面的例子:

  • ID 为integrationEvaluationContext的默认IntegrationEvaluationContextFactoryBean Bean 已在应用程序上下文中注册。

  • <spel-function/>解析并添加到functionsMap中,作为一个映射条目,其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 集成提供了 FolloiWNG 标准功能,这些功能在启动时自动注册到应用程序上下文中:

  • #jsonPath:在指定的对象上计算“jsonpath”。这个函数调用JsonPathUtils.evaluate(…​),将其委托给Jayway JsonPath 库 (opens new window)。下面的清单展示了一些使用示例:

    <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](https://github.com/json-path/jsonpath#filter-predicates)的数组,它可以通过引用 Bean 或 Bean 方法(例如)来提供。

    使用此函数需要 Jayway JsonPath 库(json-path.jar)位于 Classpath 上。
    否则将不注册#jsonPathspel 函数。

    有关 JSON 的更多信息,请参见变压器中的“JSON Transformers”。

  • #xpath:在某些提供的对象上计算“XPath”。有关 XML 和 XPath 的更多信息,请参见XML 支持-处理 XML 有效负载

# 属性访问器

Spring 集成提供了名称空间支持,以允许你创建 SPEL 自定义[PropertyAccessor](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/expression/propertyaccessor.html)实现。你可以使用<spel-property-accessors/>组件为整个框架中使用的PropertyAccessor实例提供自定义PropertyAccessor实例的列表。你可以添加一个或多个这些组件,而不是配置前面所示的工厂 Bean,并且框架会自动将访问器添加到默认的integrationEvaluationContext工厂 Bean。下面的示例展示了如何做到这一点:

<int:spel-property-accessors>
	<bean id="jsonPA" class="org.springframework.integration.json.JsonPropertyAccessor"/>
	<ref bean="fooPropertyAccessor"/>
</int:spel-property-accessors>

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

要通过使用 Java 配置提供PropertyAccessor实例,你应该声明一个名为SpelPropertyAccessorRegistrar Bean 的spelPropertyAccessorRegistrar(由IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME常量指定)。下面的示例展示了如何使用 Java 配置两个自定义PropertyAccessor实例:

@Bean
public SpelPropertyAccessorRegistrar spelPropertyAccessorRegistrar() {
    return new SpelPropertyAccessorRegistrar(new JsonPropertyAccessor())
                    .add(fooPropertyAccessor());
}
在父上下文中声明的自定义PropertyAccessor实例也可用于任意子上下文。
它们被放置在结果列表的末尾(但在默认org.springframework.context.expression.MapAccessor之前)并且o.s.expression.spel.support.ReflectivePropertyAccessor)。
如果在子上下文中声明一个具有相同 Bean ID 的PropertyAccessor,则它将覆盖父访问器。
<spel-property-accessors/>中声明的 bean 必须具有一个“id”属性。
使用的最终顺序如下:

当前上下文中的访问者,按照它们被声明为

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

MapAccessor
的顺序