spel.md 10.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
# 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/>* 的顺序|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|