Skip to content

WebFlux endpoints with Spring Integration

This page assembles a reactive Integration flow with annotations: a WebFluxInboundEndpoint serves GET /all, forwards each request through a FluxMessageChannel, and a WebFluxRequestExecutingMessageHandler calls the app's own /posts endpoint through a WebClient and streams the reply back.

Step 1: enable Integration

Add the dependency (see the chapter intro) and annotate a configuration class:

@Configuration
@EnableIntegration
@IntegrationComponentScan
public class IntegrationConfig {

    // endpoint beans below
}

Step 2: declare the inbound endpoint

@Bean
public WebFluxInboundEndpoint jsonInboundEndpoint() {
    WebFluxInboundEndpoint endpoint = new WebFluxInboundEndpoint();
    RequestMapping requestMapping = new RequestMapping();
    requestMapping.setPathPatterns("/all");
    endpoint.setRequestMapping(requestMapping);
    endpoint.setRequestChannel(fluxResultChannel());
    return endpoint;
}

@Bean
public MessageChannel fluxResultChannel() {
    return new FluxMessageChannel();
}

The endpoint maps GET /all to Integration messages and writes the replies of fluxResultChannel to the HTTP response. Because the channel is a FluxMessageChannel, the reply can be a streamed Flux with backpressure honored end to end.

Step 3: declare the reactive outbound handler

@Bean
public WebClient webClient() {
    return WebClient.builder().baseUrl("http://localhost:8080").build();
}

@ServiceActivator(inputChannel = "fluxResultChannel")
@Bean
public WebFluxRequestExecutingMessageHandler reactiveOutbound(WebClient client) {
    WebFluxRequestExecutingMessageHandler handler
            = new WebFluxRequestExecutingMessageHandler("/posts", client);
    handler.setHttpMethod(HttpMethod.GET);
    handler.setExpectedResponseType(String.class);
    return handler;
}

The @ServiceActivator subscribes the handler to the fluxResultChannel: every message that arrives on the channel triggers an HTTP GET /posts through the WebClient, and the reactive reply flows back through the inbound endpoint to the original caller.

Step 4: verify

The sample's repository serves two posts from memory, and the test asserts both the plain WebFlux route and the Integration route:

@SpringJUnitConfig(classes = Application.class)
public class IntegrationTests {

    @Autowired
    HttpServer httpServer;

    @BeforeEach
    public void setup() {
        this.disposableServer = this.httpServer.bindNow();
        this.client = WebTestClient
                .bindToServer()
                .responseTimeout(Duration.ofMillis(5000))
                .baseUrl("http://localhost:" + this.port)
                .build();
    }

    @Test
    public void getAllPostsWillBeOk() {
        this.client
                .get()
                .uri("/posts")
                .exchange()
                .expectStatus().isOk();
    }

    @Test
    public void getAllPostsWillBeOk_viaIntegration() {
        this.client
                .get()
                .uri("/all")
                .exchange()
                .expectStatus().isOk();
    }
}

You can explore the complete codes in spring-reactive-sample/integration.

What's next

The annotation style works, but the wiring is verbose. The Spring Integration Java DSL expresses the same flow in a few fluent lines.