Appendix A. Spring Expression Language (SpEL)

You can configure many Spring Integration components by using expressions written in the Spring Expression Language.

In most cases, the #root object is the Message, which has two properties (headers and payload) that allow such expressions as payload, payload.thing, headers['my.header'], and so on.

In some cases, additional variables are provided. For example, <int-http:inbound-gateway/> provides #requestParams (parameters from the HTTP request) and #pathVariables (values from path placeholders in the URI).

For all SpEL expressions, a BeanResolver is available to enable references to any bean in the application context (for example, @myBean.foo(payload)). In addition, two PropertyAccessors are available. A MapAccessor enables accessing values in a Map by using a key and a ReflectivePropertyAccessor, which allows access to fields and JavaBean compliant properties (by using getters and setters). This is how you can access the Message headers and payload properties.

A.1 SpEL Evaluation Context Customization

Starting with Spring Integration 3.0, you can add additional PropertyAccessor instances to the SpEL evaluation contexts used by the framework. The framework provides the (read-only) JsonPropertyAccessor, which you can use to access fields from a JsonNode or JSON in a String. You can also create your own PropertyAccessor if you have specific needs.

In addition, you can add custom functions. Custom functions are static methods declared on a class. Functions and property accessors are available in any SpEL expression used throughout the framework.

The following configuration shows how to directly configure the IntegrationEvaluationContextFactoryBean with custom property accessors and functions:

<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>

For convenience, Spring Integration provides namespace support for both property accessors and functions, as described in the following sections. The framework automatically configures the factory bean on your behalf.

This factory bean definition overrides the default integrationEvaluationContext bean definition. It adds the custom accessor and one custom function to the list (which also includes the standard accessors mentioned earlier).

Note that custom functions are static methods. In the preceding example, the custom function is a static method called calc on a class called MyFunctions and takes a single parameter of type MyThing.

Suppose you have a Message with a payload that has a type of MyThing. Further suppose that you need to perform some action to create an object called MyObject from MyThing and then invoke a custom function called calc on that object.

The standard property accessors do not know how to get a MyObject from a MyThing, so you could write and configure a custom property accessor to do so. As a result, your final expression might be "#barcalc(payload.myObject)".

The factory bean has another property (typeLocator), which lets you customize the TypeLocator used during SpEL evaluation. You might need to do so running in some environments that use a non-standard ClassLoader. In the following example, SpEL expressions always use the bean factory’s class loader:

<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>

A.2 SpEL Functions

Spring Integration provides namespace support to let you create SpEL custom functions. You can specify <spel-function/> components to provide custom SpEL functions to the EvaluationContext used throughout the framework. Instead of configuring the factory bean shown earlier, you can add one or more of these components, and the framework automatically adds them to the default integrationEvaluationContext factory bean.

For example, suppose you have a useful static method to evaluate XPath. The following example shows how you can create a custom function to use that method:

<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)" />

Given the preceding example:

  • The default IntegrationEvaluationContextFactoryBean bean with an ID of integrationEvaluationContext is registered with the application context.
  • The <spel-function/> is parsed and added to the functions Map of integrationEvaluationContext as a map entry with its id as the key and the static Method as the value.
  • The integrationEvaluationContext factory bean creates a new StandardEvaluationContext instance, and it is configured with the default PropertyAccessor instances, a BeanResolver, and the custom functions.
  • That EvaluationContext instance is injected into the ExpressionEvaluatingTransformer bean.

To provide a SpEL Function by using Java configuration, you can declare a SpelFunctionFactoryBean bean for each function. The following example shows how to create a custom function:

@Bean
public SpelFunctionFactoryBean xpath() {
    return new SpelFunctionFactoryBean(XPathUtils.class, "evaluate");
}
[Note]Note

SpEL functions declared in a parent context are also made available in any child contexts. Each context has its own instance of the integrationEvaluationContext factory bean, because each needs a different BeanResolver, but the function declarations are inherited and can be overridden by declaring a SpEL function with the same name.

A.2.1 Built-in SpEL Functions

Spring Integration provides the folloiwng standard functions, which are registered with the application context automatically on start up:

  • #jsonPath: Evaluates a jsonPath on a specified object. This function invokes JsonPathUtils.evaluate(...), which delegates to the Jayway JsonPath library. The following listing shows some usage examples:

    <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 also supports a third (optional) parameter: an array of com.jayway.jsonpath.Filter, which can be provided by a reference to a bean or bean method (for example).

    [Note]Note

    Using this function requires the Jayway JsonPath library (json-path.jar) to be on the classpath. Otherwise the #jsonPath SpEL function is not registered.

    For more information regarding JSON see JSON Transformers in Section 9.1, “Transformer”.

  • #xpath: To evaluate an xpath on some provided object. For more information regarding XML and XPath, see Chapter 38, XML Support - Dealing with XML Payloads.

A.3 Property Accessors

Spring Integration provides namespace support to let you create SpEL custom PropertyAccessor implementations. You can use the <spel-property-accessors/> component to provide a list of custom PropertyAccessor instances to the EvaluationContext used throughout the framework. Instead of configuring the factory bean shown earlier, you can add one or more of these components, and the framework automatically adds the accessors to the default integrationEvaluationContext factory bean. The following example shows how to do so:

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

In the preceding example, two custom PropertyAccessor instances are injected into the EvaluationContext (in the order in which they are declared).

To provide PropertyAccessor instances by using Java Configuration, you should declare a SpelPropertyAccessorRegistrar bean with a name of spelPropertyAccessorRegistrar (dictated by the IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME constant). The following example shows how to configure two custom PropertyAccessor instances with Java:

@Bean
public SpelPropertyAccessorRegistrar spelPropertyAccessorRegistrar() {
    return new SpelPropertyAccessorRegistrar(new JsonPropertyAccessor())
                    .add(fooPropertyAccessor());
}
[Note]Note

Custom PropertyAccessor instances declared in a parent context are also made available in any child contexts. They are placed at the end of result list (but before the default org.springframework.context.expression.MapAccessor and o.s.expression.spel.support.ReflectivePropertyAccessor). If you declare a PropertyAccessor with the same bean ID in a child context, it overrides the parent accessor. Beans declared within a <spel-property-accessors/> must have an id attribute. The final order of usage is as follows:

  • The accessors in the current context, in the order in which they are declared
  • Any accessors from parent contexts, in order
  • The MapAccessor
  • The ReflectivePropertyAccessor