java.util.function Interfaces Support

Starting with version 5.1, Spring Integration provides direct support for interfaces in the java.util.function package. All messaging endpoints, (Service Activator, Transformer, Filter, etc.) can now refer to Function (or Consumer) beans. The Messaging Annotations can be applied directly on these beans similar to regular MessageHandler definitions. For example if you have this Function bean definition:

@Configuration
public class FunctionConfiguration {

    @Bean
    public Function<String, String> functionAsService() {
        return String::toUpperCase;
    }

}

You can use it as a simple reference in an XML configuration file:

<service-activator input-channel="processorViaFunctionChannel" ref="functionAsService"/>

When we configure our flow with Messaging Annotations, the code is straightforward:

@Bean
@Transformer(inputChannel = "functionServiceChannel")
public Function<String, String> functionAsService() {
    return String::toUpperCase;
}

When the function returns an array, Collection (essentially, any Iterable), Stream or Reactor Flux, @Splitter can be used on such a bean to perform iteration over the result content.

The java.util.function.Consumer interface can be used for an <int:outbound-channel-adapter> or, together with the @ServiceActivator annotation, to perform the final step of a flow:

@Bean
@ServiceActivator(inputChannel = "messageConsumerServiceChannel")
public Consumer<Message<?>> messageConsumerAsService() {
    // Has to be an anonymous class for proper type inference
    return new Consumer<Message<?>>() {

        @Override
        public void accept(Message<?> e) {
            collector().add(e);
        }

    };
}

Also, pay attention to the comment in the code snippet above: if you would like to deal with the whole message in your Function/Consumer you cannot use a lambda definition. Because of Java type erasure we cannot determine the target type for the apply()/accept() method call.

The java.util.function.Supplier interface can simply be used together with the @InboundChannelAdapter annotation, or as a ref in an <int:inbound-channel-adapter>:

@Bean
@InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000"))
public Supplier<String> pojoSupplier() {
    return () -> "foo";
}

With the Java DSL we just need to use a reference to the function bean in the endpoint definitions. Meanwhile, an implementation of the Supplier interface can be used as regular MessageSource definition:

@Bean
public Function<String, String> toUpperCaseFunction() {
    return String::toUpperCase;
}

@Bean
public Supplier<String> stringSupplier() {
    return () -> "foo";
}

@Bean
public IntegrationFlow supplierFlow() {
    return IntegrationFlow.from(stringSupplier())
                .transform(toUpperCaseFunction())
                .channel("suppliedChannel")
                .get();
}

This function support is useful when used together with the Spring Cloud Function framework, where we have a function catalog and can refer to its member functions from an integration flow definition.

Kotlin Lambdas

The Framework also has been improved to support Kotlin lambdas for functions, so now you can use a combination of the Kotlin language and Spring Integration flow definitions:

@Bean
@Transformer(inputChannel = "functionServiceChannel")
fun kotlinFunction(): (String) -> String {
    return { it.toUpperCase() }
}

@Bean
@ServiceActivator(inputChannel = "messageConsumerServiceChannel")
fun kotlinConsumer(): (Message<Any>) -> Unit {
    return { print(it) }
}

@Bean
@InboundChannelAdapter(value = "counterChannel",
        poller = [Poller(fixedRate = "10", maxMessagesPerPoll = "1")])
fun kotlinSupplier(): () -> String {
    return { "baz" }
}