Spring 表达式语言 (SpEL)

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

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

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

对于所有 SpEL 表达式,都提供了一个 BeanResolver 来启用对应用程序上下文中的任何 bean 的引用(例如,@myBean.foo(payload))。此外,还提供了两个 PropertyAccessorsMapAccessor 允许使用键访问 Map 中的值,而 ReflectivePropertyAccessor 允许访问字段和符合 JavaBean 规范的属性(使用 getter 和 setter)。这就是您可以访问 Message 的标头和有效负载属性的方式。

SpEL 评估上下文定制

从 Spring Integration 3.0 开始,您可以将其他 PropertyAccessor 实例添加到框架使用的 SpEL 评估上下文中。框架提供了(只读)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 Integration 为属性访问器和函数都提供了命名空间支持,如以下部分所述。框架会自动为您配置工厂 bean。

此工厂 bean 定义覆盖了默认的 integrationEvaluationContext bean 定义。它将自定义访问器和一个自定义函数添加到列表中(其中还包括前面提到的标准访问器)。

请注意,自定义函数是静态方法。在前面的示例中,自定义函数是一个名为 calc 的静态方法,它位于名为 MyFunctions 的类中,并接受一个类型为 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/> 组件来提供 自定义 SpEL 函数 到整个框架中使用的 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)" />

鉴于前面的示例

  • 默认的 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 中。以下示例展示了如何执行此操作

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

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

要使用 Java 配置提供 PropertyAccessor 实例,您应该声明一个名为 spelPropertyAccessorRegistrarSpelPropertyAccessorRegistrar bean(由 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.MapAccessoro.s.expression.spel.support.ReflectivePropertyAccessor 之前)。如果您在子上下文中声明了具有相同 bean ID 的 PropertyAccessor,它将覆盖父访问器。在 <spel-property-accessors/> 中声明的 bean 必须具有 'id' 属性。最终的使用顺序如下

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

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

  • MapAccessor

  • ReflectivePropertyAccessor