Search code examples
spring-integrationspring-el

access private field in recipientflow expression


A quick question on SPEL syntax in Spring Integration.

In the following code sample, what's the correct way to access myMemberwithin a SPEL expression?

@Component
@RequiredArgsConstructor //Lombok
public class MyClass(

    // autowired member
    private final MyMember myMember;

    @Bean
    public IntegrationFlow myFlow() {
        return IntegrationFlows
            .from(...)
            .routeToRecipients(
                r -> r.ignoreSendFailures(false)
                      .recipientFlow(...)
                      // #this and #root both refer to the message and give access
                      // to payload and headers, but can I also access myMember
                      // in this SPEL expression?
                      .recipientFlow("@#?...myMember.method(...)", // returns true or false
                                     subflow())
            ) 
            ...
            .get();
}

I can of course easily work this around by setting a header with the results of myMember.method(...) beforehand, but I was wondering if I could use a shortcut.


Solution

  • The reference documentation gives a few examples of recipient methods to use in the RecipientListRouterSpec parameter of the routeToRecipients method:

    @Bean
    public IntegrationFlow recipientListFlow() {
        return IntegrationFlows.from("recipientListInput")
                .<String, String>transform(p -> p.replaceFirst("Payload", ""))
                .routeToRecipients(r -> r
                        .recipient("thing1-channel", "'thing1' == payload")
                        .recipientMessageSelector("thing2-channel", m ->
                                m.getHeaders().containsKey("recipient")
                                        && (boolean) m.getHeaders().get("recipient"))
                        .recipientFlow("'thing1' == payload or 'thing2' == payload or 'thing3' == payload",
                                f -> f.<String, String>transform(String::toUpperCase)
                                        .channel(c -> c.queue("recipientListSubFlow1Result")))
                        .recipientFlow((String p) -> p.startsWith("thing3"),
                                f -> f.transform("Hello "::concat)
                                        .channel(c -> c.queue("recipientListSubFlow2Result")))
                        .recipientFlow(new FunctionExpression<Message<?>>(m ->
                                        "thing3".equals(m.getPayload())),
                                f -> f.channel(c -> c.queue("recipientListSubFlow3Result")))
                        .defaultOutputToParentFlow())
                .get();
    } 
    

    The last example of the recipientFlow methods above is the one that takes an Expression and an IntegrationFlow as parameters. This integration flow is invoked only if the expression evaluates to true.

    A FunctionExpression can be used to solve the OP:

        @Component
        @RequiredArgsConstructor //Lombok
        public class MyClass(
        
            // autowired member
            private final MyMember myMember;
        
            @Bean
            public IntegrationFlow myFlow() {
                return IntegrationFlows
                    .from(...)
                    .routeToRecipients(
                        r -> r.ignoreSendFailures(false)
                              .recipientFlow(...)
                              .recipientFlow(new FunctionExpression<Message<?>>(
                                  m -> myMember.method(...), // returns true or false
                                  subflow())
                    ) 
                    ...
                    .get();
        }