Spring 運算式語言 (SpEL)

您可以使用以 Spring 運算式語言撰寫的運算式,來設定許多 Spring Integration 元件。

在大多數情況下,#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 為屬性存取器和函數提供命名空間支援,如下節所述。框架會自動為您設定 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/> 元件,以將 自訂 SpEL 函數 提供給整個框架中使用的 EvaluationContext。您可以新增一或多個這些元件,而不是設定先前顯示的 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/> 已剖析並新增至 integrationEvaluationContextfunctions Map,作為地圖項目,其 id 作為鍵,靜態 Method 作為值。

  • 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 方法 (例如) 提供。

    使用此函數需要 Jayway JsonPath 程式庫 (json-path.jar) 位於類別路徑中。否則,不會註冊 #jsonPath SpEL 函數。

    有關 JSON 的更多資訊,請參閱 轉換器 中的「JSON 轉換器」。

  • #xpath:在某些提供的物件上評估 xpath。有關 XML 和 XPath 的更多資訊,請參閱 XML 支援 - 處理 XML 有效負載

屬性存取器

Spring Integration 提供命名空間支援,讓您建立 SpEL 自訂 PropertyAccessor 實作。您可以使用 <spel-property-accessors/> 元件,將自訂 PropertyAccessor 實例的清單提供給整個框架中使用的 EvaluationContext。您可以新增一或多個這些元件,而不是設定先前顯示的 factory bean,框架會自動將存取器新增至預設的 integrationEvaluationContext factory 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