Tutorial

Spring WebFlux - Spring Reactive Programming

Published on August 4, 2022
author

Pankaj

Spring WebFlux - Spring Reactive Programming

Spring WebFlux is the new module introduced in Spring 5. Spring WebFlux is the first step towards reactive programming model in spring framework.

Spring Reactive Programming

If you are new to reactive programming model, then I would highly suggest you to go through following articles to learn about reactive programming.

If you are new to Spring 5, please go through Spring 5 Features.

Spring WebFlux

Spring WebFlux is the alternative to Spring MVC module. Spring WebFlux is used to create fully asynchronous and non-blocking application built on event-loop execution model. Below diagram from Spring Official Documentation provides great insight on comparison of Spring WebFlux to Spring Web MVC. spring webflux and spring mvc If you are looking to develop a web application or Rest web service on non-blocking reactive model, then you can look into Spring WebFlux. Spring WebFlux is supported on Tomcat, Jetty, Servlet 3.1+ containers, as well as on non-Servlet runtimes such as Netty and Undertow. Spring WebFlux is built on Project Reactor. Project Reactor is the implementation of Reactive Streams specification. Reactor provides two types:

  1. Mono: implements Publisher and returns 0 or 1 elements
  2. Flux: implements Publisher and returns N elements.

Spring WebFlux Hello World Example

Let’s built a simple Spring WebFlux Hello World application. We will create a simple rest web service and use Spring Boot to run it on default Netty server. Our final project structure looks like below image. spring webflux example Let’s look into each component of the application one by one.

Spring WebFlux Maven Dependencies

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.journaldev.spring</groupId>
  <artifactId>SpringWebflux</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>Spring WebFlux</name>
  <description>Spring WebFlux Example</description>
  
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <jdk.version>1.9</jdk.version>
    </properties>
    
  <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>io.projectreactor</groupId>
			<artifactId>reactor-test</artifactId>
			<scope>test</scope>
		</dependency>
    </dependencies>
	<repositories>
		<repository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
	<pluginRepositories>
		<pluginRepository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</pluginRepository>
		<pluginRepository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                    <configuration>
                        <source>${jdk.version}</source>
                        <target>${jdk.version}</target>
                    </configuration>
                </plugin>
            </plugins>
    </pluginManagement>
    </build>
    
</project>

The most important dependencies are spring-boot-starter-webflux and spring-boot-starter-parent. Some other dependencies are for creating JUnit test cases.

Spring WebFlux Handler

Spring WebFlux Handler method handles the request and returns Mono or Flux as response.

package com.journaldev.spring.component;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

import reactor.core.publisher.Mono;

@Component
public class HelloWorldHandler {

	public Mono<ServerResponse> helloWorld(ServerRequest request) {
		return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
			.body(BodyInserters.fromObject("Hello World!"));
	}
}

Notice that reactive component Mono holds the ServerResponse body. Also look at the function chain to set the return content type, response code and body.

Spring WebFlux Router

Router method are used to define routes for the application. These methods return RouterFunction object that also holds ServerResponse body.

package com.journaldev.spring.component;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
public class HelloWorldRouter {

	@Bean
	public RouterFunction<ServerResponse> routeHelloWorld(HelloWorldHandler helloWorldHandler) {

		return RouterFunctions.route(RequestPredicates.GET("/helloWorld")
                .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), helloWorldHandler::helloWorld);
	}
}

So we are exposing a GET method for /helloWorld and the client call should accept plain text response.

Spring Boot Application

Let’s configure our simple WebFlux application with Spring Boot.

package com.journaldev.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

If you look at above code, there is nothing related to Spring WebFlux. But Spring Boot will configure our application as Spring WebFlux since we have added dependency of spring-boot-starter-webflux module.

Java 9 Modules Support

Our application is ready to execute on Java 8, but if you are using Java 9 then we also need to add module-info.java class.

module com.journaldev.spring {
    requires reactor.core;
    requires spring.web;
    requires spring.beans;
    requires spring.context;
    requires spring.webflux;
    requires spring.boot;
    requires spring.boot.autoconfigure;
    exports com.journaldev.spring;
}

Running the Spring WebFlux Spring Boot App

If you have Spring support in Eclipse, then you can run above class as Spring Boot App. Eclipse run as spring boot app If you like to use command line, then open terminal and run command mvn spring-boot:run from the project source directory. Once the app is running, notice following log messages to make sure everything is good with our app. It’s also helpful when you extend this simple app by adding more routes and functionalities.

2018-05-07 15:01:47.893  INFO 25158 --- [           main] o.s.w.r.f.s.s.RouterFunctionMapping      : Mapped ((GET && /helloWorld) && Accept: [text/plain]) -> com.journaldev.spring.component.HelloWorldRouter$$Lambda$501/704766954@6eeb5d56
2018-05-07 15:01:48.495  INFO 25158 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext     : Started HttpServer on /0:0:0:0:0:0:0:0:8080
2018-05-07 15:01:48.495  INFO 25158 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2018-05-07 15:01:48.501  INFO 25158 --- [           main] com.journaldev.spring.Application        : Started Application in 1.86 seconds (JVM running for 5.542)

It’s clear from logs that our app is running on Netty server on port 8080. Let’s go ahead and test our application.

Spring WebFlux App Test

We can test our app with various methods.

  1. Using CURL command

    $ curl https://localhost:8080/helloWorld
    Hello World!
    $ 
    
  2. Launch URL in Browser spring webflux restful web service test

  3. Using WebTestClient from Spring 5 Here is a JUnit test program to test our Rest web service using WebTestClient from Spring 5 reactive web.

    package com.journaldev.spring;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.reactive.server.WebTestClient;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class SpringWebFluxTest {
    
    	@Autowired
    	private WebTestClient webTestClient;
    
    	
    	@Test
    	public void testHelloWorld() {
    		webTestClient
    		.get().uri("/helloWorld") // GET method and URI
    		.accept(MediaType.TEXT_PLAIN) //setting ACCEPT-Content
    		.exchange() //gives access to response
    		.expectStatus().isOk() //checking if response is OK
    		.expectBody(String.class).isEqualTo("Hello World!"); // checking for response type and message
    	}
    
    }
    

    Run it a JUnit test case and it should pass with flying colors. Spring Reactive WebTestClient example JUnit test case

  4. Using WebClient from Spring Web Reactive We can also use WebClient to call the REST web service.

    package com.journaldev.spring.client;
    
    import org.springframework.http.MediaType;
    import org.springframework.web.reactive.function.client.ClientResponse;
    import org.springframework.web.reactive.function.client.WebClient;
    
    import reactor.core.publisher.Mono;
    
    public class HelloWorldWebClient {
    
    	public static void main(String args[]) {
    		WebClient client = WebClient.create("https://localhost:8080");
    
    		Mono<ClientResponse> result = client.get()
    				.uri("/helloWorld")
    				.accept(MediaType.TEXT_PLAIN)
    				.exchange();
    
    			System.out.println("Result = " + result.flatMap(res -> res.bodyToMono(String.class)).block());
    	}
    	
    }
    

    Just run it as a simple java application and you should see the proper output with a lot of debug messages. spring reactive web WebClient example

Summary

In this post we learned about Spring WebFlux and how to build a hello world reactive Restful web service. It’s good to see that popular frameworks such as Spring are rooting for reactive programming model. But we have a lot to cover because if all your dependencies are not reactive and non-blocking then your application is also not truly reactive. For example, relational database vendors doesn’t have reactive drivers because they depend on JDBC, that is not reactive. Hence Hibernate API is also non-reactive. So if you are using relational databases then you can’t built a truly reactive application, yet. I am hopeful that it will change sooner than later.

You can download the project code from my GitHub Repository.

Reference: Official Documentation

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Pankaj

author

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
JournalDev
DigitalOcean Employee
DigitalOcean Employee badge
August 30, 2019

return Whitelabel Error Page, why ?

- sunpiny

    JournalDev
    DigitalOcean Employee
    DigitalOcean Employee badge
    July 17, 2019

    Nice post! I’ve one question, how do we deploy a Spring WebFlux project in production? If we use Spring WebFlux with Spring boot, we can’t create a WAR file which we could deploy in say tomcat. Do we have to create a fat jar file and run it using java -jar command?

    - Asraar

      JournalDev
      DigitalOcean Employee
      DigitalOcean Employee badge
      March 12, 2019

      Thanks for this post! Simple and Neat webFlux starter app with unit test sample and webclient sample programs. Summary is cherry on cake for beginners.

      - Krishma

        JournalDev
        DigitalOcean Employee
        DigitalOcean Employee badge
        December 11, 2018

        I am getting this error : Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation] and output is >> result = null because of above issue. Could you please let me know, what is issue?

        - Muna

          Try DigitalOcean for free

          Click below to sign up and get $200 of credit to try our products over 60 days!

          Sign up

          Join the Tech Talk
          Success! Thank you! Please check your email for further details.

          Please complete your information!

          Become a contributor for community

          Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

          DigitalOcean Documentation

          Full documentation for every DigitalOcean product.

          Resources for startups and SMBs

          The Wave has everything you need to know about building a business, from raising funding to marketing your product.

          Get our newsletter

          Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

          New accounts only. By submitting your email you agree to our Privacy Policy

          The developer cloud

          Scale up as you grow — whether you're running one virtual machine or ten thousand.

          Get started for free

          Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

          *This promotional offer applies to new accounts only.