# Spring 表达式语言

##  Spring 表达式语言

你可以通过使用[Spring Expression Language](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#expressions)中编写的表达式来配置许多 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)`）。此外，还有两个`PropertyAccessors`可用。`MapAccessor`允许通过使用一个键和`ReflectivePropertyAccessor`访问`Map`中的值，这允许访问字段和 JavaBean 兼容的属性（通过使用 getter 和 setter）。这就是访问`Message`标头和有效负载属性的方法。

### spel 评估上下文定制

从 Spring Integration3.0 开始，你可以向框架使用的 SPEL 评估上下文添加额外的`PropertyAccessor`实例。该框架提供（只读）`JsonPropertyAccessor`，你可以使用它从`JsonNode`或`String`中的 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 定义。它将自定义访问器和一个自定义函数添加到列表中（其中还包括标准访问器[前面提到过](#spel)）。

请注意，自定义函数是静态方法。在前面的示例中，自定义函数是在一个名为`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 函数](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#expressions-ref-functions)提供`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/>`解析并添加到`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 函数也可以在任意子上下文中使用，<br/>每个上下文都有自己的`integrationEvaluationContext`工厂 Bean 实例，因为每个上下文都需要一个不同的`BeanResolver`，但是函数声明是继承的，可以通过声明同名的 SPEL 函数来重写。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 内置 spel 函数

Spring 集成提供了 FolloiWNG 标准功能，这些功能在启动时自动注册到应用程序上下文中：

* `#jsonPath`:在指定的对象上计算“jsonpath”。这个函数调用`JsonPathUtils.evaluate(…​)`，将其委托给[Jayway JsonPath 库](https://github.com/json-path/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`]（https://github.com/json-path/jsonpath#filter-predicates）的数组，它可以通过引用 Bean 或 Bean 方法（例如）来提供。

  |   |使用此函数需要 Jayway JsonPath 库（`json-path.jar`）位于 Classpath 上。<br/>否则将不注册`#jsonPath`spel 函数。|
  |---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|

  有关 JSON 的更多信息，请参见[变压器](./transformer.html#transformer)中的“JSON Transformers”。

* `#xpath`:在某些提供的对象上计算“XPath”。有关 XML 和 XPath 的更多信息，请参见[XML 支持-处理 XML 有效负载](./xml.html#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`实例也可用于任意子上下文。<br/>它们被放置在结果列表的末尾（但在默认`org.springframework.context.expression.MapAccessor`之前）并且`o.s.expression.spel.support.ReflectivePropertyAccessor`）。<br/>如果在子上下文中声明一个具有相同 Bean ID 的`PropertyAccessor`，则它将覆盖父访问器。<br/>在`<spel-property-accessors/>`中声明的 bean 必须具有一个“id”属性。<br/>使用的最终顺序如下：<br/><br/>当前上下文中的访问者，按照它们被声明为<br/><br/>* 来自父上下文的任何访问器的顺序，按照<br/><br/>*`MapAccessor`<br/>* 的顺序|
|---| |