spring

Introduction to Spring Webflux

Welcome, in this tutorial, we will see how to create a simple spring boot reactive programming application with webflux.

1. Introduction

Before going further in this tutorial, let us understanding webflux and reactive programming.

1.1 Webflux

  • Webflux is the reactive-web framework in spring and was added as a part of the 5.0 release
  • It offers asynchronous, non-blocking, and event-driven approach for the data processing
  • It also offers reactive streams back-pressure and runs on servers like Netty, Undertow, or the Servlet 3.1 containers

1.2 Reactive programming

  • Reactive programming is a functional, event-based, asynchronous, non-blocking data processing technique
  • The programming technique refers to reacting to the changes such as mouse clicks or the I/O events
  • The reactive programming application works better and more efficiently when working with the stream of data. The client application consumes it and streams it back to the users
  • The streaming api provides 4 different interfaces –
    • Publisher – Emits a sequence of events to subscribers according to the subscriber demands
    • Subscriber – Receives and processes the events emitted by the publisher interface. Multiple subscribers can be linked to a single publisher and can respond differently to the same event
    • Subscription – Defines a 1-1 relationship between the publisher and subscriber interfaces. Each subscriber can only be linked to a single publisher
    • Processor – Shows a processing stage consisting of both subscriber and publisher interface. It also obeys the contract of both the interfaces

1.3 Spring webflux

  • Spring webflux is similar to spring MVC and supports non-blocking reactive streams. It also supports the back-pressure concept and uses Netty as the preferred server to run reactive applications
  • Uses the project reactor as the reactive library
  • It uses 2 publishers –
    • Mono returns 0 or 1 element
    • Flux returns 0 to N elements. It is endless and can emit events forever. It also sends a complete notification when the last event is sent
  • Allows scalability and immunity to latency problems in a microservice architecture. It also offers excellent stream processing capabilities

1.4 Lombok

  • Lombok is nothing but a small library that reduces the amount of boilerplate Java code from the project
  • Automatically generates the getters and setters for the object by using the Lombok annotations
  • Hooks in via the Annotation processor API
  • Raw source code is passed to Lombok for code generation before the Java Compiler continues. Thus, produces properly compiled Java code in conjunction with the Java Compiler
  • Under the target/classes folder you can view the compiled class files
  • Can be used with Maven, Gradle IDE, etc.

1.4.1 Lombok features

FeatureDetails
valLocal variables are declared as final
varMutable local variables
@Slf4JCreates an SLF4J logger
@CleanupWill call close() on the resource in the finally block
@GetterCreates getter methods for all properties
@SetterCreates setter for all non-final properties
@EqualsAndHashCode
  • Generates implementations of equals(Object other) and hashCode()
  • By default will use all non-static, non-transient properties
  • Can optionally exclude specific properties
@ToString
  • Generates String of class name, and each field separated by commas
  • Optional parameter to include field names
  • Optional parameter to include a call to the super toString method
@NoArgsConstructor
  • Generates no-args constructor
  • Will cause compiler error if there are final fields
  • Can optionally force, which will initialize final fields with 0/false/null var – mutable local variables
@RequiredArgsContructor
  • Generates a constructor for all fields that are final or marked @NonNull
  • The constructor will throw a NullPointerException if any @NonNull fields are null val – local variables are declared final
@AllArgsConstructor
  • Generates a constructor for all properties of the class
  • Any @NotNull properties will have null checks
@Data
  • Generates typical boilerplate code for POJOs
  • Combines – @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor
  • No constructor is generated if constructors have been explicitly declared
@Builder
  • Implements the Builder pattern for object creation
@Value
  • The immutable variant of @Data
  • All fields are made private and final by default

1.5 Spring boot starters

  • Spring boot provides several starter libraries which are used that ease the development of the spring application
  • In spring the stater name starts with a prefix – spring-boot-starter-* where * denotes a particular type of application

Let us see some of the commonly used spring boot stater libraries.

NameDescription
spring-boot-starter-thymeleafUsed to build MVC web applications using Thymeleaf views
spring-boot-starter-data-redisUsed for Redis key-value data store with Spring Data Redis and the Jedis client
spring-boot-starter-webUsed for building the web and RESTful applications. It uses Tomcat as the default embedded container
spring-boot-starter-testUsed to test Spring Boot applications with libraries, including JUnit, Hamcrest, and Mockito
spring-boot-starter-hateoasUsed to build a hypermedia-based RESTful application with Spring HATEOAS
spring-boot-starter-aopUsed for aspect-oriented programming with Spring AOP and AspectJ
spring-boot-starter-amqpUsed for Spring AMQP and Rabbit MQ
spring-boot-starter-securityUsed for Spring Security
spring-boot-starter-data-jpaUsed for Spring Data JPA with Hibernate
spring-boot-starterUsed for core starter, including auto-configuration support, logging, and YAML
spring-boot-starter-webfluxUsed for reactive programming in the spring framework. It uses Netty as the default embedded container

2. Introduction to Spring Webflux

Let us go ahead with the tutorial implementation but before going any further I’m assuming that you’re aware of the Spring boot basics.

2.1 Tools Used for Spring boot application and Project Structure

We are using Eclipse Kepler SR2, JDK 8, and Maven. In case you’re confused about where you should create the corresponding files or folder, let us review the project structure of the spring boot application.

spring webflux - project structure
Fig. 1: Project structure

Let us start building the application!

3. Creating a Spring Boot application

Below are the steps involved in developing the application.

3.1 Maven dependency

Here, we specify the dependency for the Spring boot (Webflux and Thymeleaf), Java Faker (to generate the dummy data), and Lombok. The updated file will have the following code.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>jcg.springboot.webflux</groupId>
    <artifactId>SpringbootWebflux</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>SpringbootWebflux</name>
    <description>Demo project for webflux in spring boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- webflux reactive -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <!-- thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.javafaker</groupId>
            <artifactId>javafaker</artifactId>
            <version>1.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.2 Application properties file

Create a new YML file at the location: SpringbootWebflux/src/main/resources/ and add the following code. You’re free to change the application details as per your wish.

application.properties

server.port=9090
spring.application.name=springboot-webflux

3.3 Java classes

Let us write the important java class(es) involved in this application. For brevity, we will skip the following classes –

  • Employee.java – Model class
  • EmployeeResponse.java – Mapper for the model class
  • FakerConfig.java – Configuration class to define the Faker bean

3.3.1 Implementation/Main class

Add the following code to the main class to bootstrap the application from the main method. Always remember, the entry point of the spring boot application is the class containing @SpringBootApplication annotation and the static main method.

SpringbootWebfluxApplication.java

package jcg.springboot.webflux;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//lombok annotation
@Slf4j
//spring annotation
@SpringBootApplication
public class SpringbootWebfluxApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebfluxApplication.class, args);
        log.info("Spring boot and webflux application started successfully.");
    }
}

3.3.2 Reactive Repository class

Add the following code to the reactive repository class. The class consists of the method to fetch the details from a backend resource and send it back to the service method. For an easy setup, it will return the mock data while in the ideal case the data would be fetched from the queue where some subscribers would continuously put the data in the given topic.

ReactiveEmployeeRepository.java

package jcg.springboot.webflux.repository;

import com.github.javafaker.Faker;
import jcg.springboot.webflux.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
import java.util.UUID;

@Repository
public class ReactiveEmployeeRepository {

    private static final String[] GENDER = {"Male", "Female", "Not to specify"};
    private static final Random RANDOM = new Random();

    @Autowired
    private Faker faker;

    public Employee find() {
        //creating a dummy employee object and returning it.
        //ideally the data would be fetched from the rabbitmq where a subscriber would continuously
        //put in the queue.
        return createDummy();
    }

    private Employee createDummy() {
        final String firstName = faker.name().firstName();
        final String lastName = faker.name().lastName();
        final String emailAddress = firstName.toLowerCase() + "." + lastName.toLowerCase() + "@automation.com";
        final String gender = GENDER[RANDOM.nextInt(GENDER.length)];
        final LocalDateTime dateTime = LocalDateTime.parse(LocalDateTime.now().toString(),
                DateTimeFormatter.ISO_DATE_TIME);

        return Employee.builder()
                .id(RANDOM.nextInt())
                .firstName(firstName)
                .lastName(lastName)
                .gender(gender)
                .emailAddress(emailAddress)
                .phoneNumber(faker.phoneNumber().cellPhone())
                .createdAt(dateTime)
                .referenceId(UUID.randomUUID())
                .build();
    }
}

3.3.3 Employee Service class

Add the following code to the service class. The method would have a return type of Flux. The flux would emit out an event every 2 seconds. Once the last event is returned a complete notification would be sent.

EmployeeService.java

package jcg.springboot.webflux.service;

import jcg.springboot.webflux.repository.ReactiveEmployeeRepository;
import jcg.springboot.webflux.response.EmployeeResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.time.Duration;

//lombok annotation
@Slf4j
//spring annotation
@Service
public class EmployeeService {

    @Autowired
    ReactiveEmployeeRepository repository;

    public Flux<EmployeeResponse> getEvents() {
        log.info("Fetching the data from the queue or database.");
        return Flux.<EmployeeResponse>generate(synchronousSink -> {
            //for testing purpose we are keeping it simple
            //in ideal case the data needs to be pulled back from a queue for continuous emit
            final EmployeeResponse response = EmployeeResponse.from(repository.find());
            log.info("Returning employee with id = {}", response.getId());
            synchronousSink.next(response);
        }).delayElements(Duration.ofSeconds(2));
    }
}

3.3.4 Flux REST Controller class

Add the following code to the REST controller. The method will fetch the data from the service and return an event stream. You can hit the controller endpoint (http://localhost:9090/api/stream/employees) in the browser to verify the non-blocking response feature of the webflux. The results will appear one by one after a delay of 2 seconds in an event form.

FluxRestController.java

package jcg.springboot.webflux.controller;

import jcg.springboot.webflux.response.EmployeeResponse;
import jcg.springboot.webflux.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

//lombok annotation
@Slf4j
//spring annotations
@RestController
@RequestMapping(value = "/api/stream")
public class FluxRestController {

    @Autowired
    private EmployeeService service;

    //url - http://localhost:9090/api/stream/employees
    @GetMapping(value = "/employees", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<EmployeeResponse> streamStaticEmployeeEvents() {
        log.info("Streaming static employee events for flux.");
        return service.getEvents();
    }
}

Similarly, you can create an index controller that can interact with the thymeleaf page to get the events beautifully. The thymeleaf controller will be available at the following url – http://localhost:9090/ in the browser. Refer to section 3.4 to create a simple HTML page that will be used to fetch the event stream from the backend.

IndexController.java

package jcg.springboot.webflux.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {

    //url - http://localhost:9090/
    @GetMapping(value = "/")
    public String index() {
        return "index";
    }
}

3.4 Thymeleaf page for Index Controller

Add the following code to the index page which will fetch the stream of employee events from the REST controller endpoint, parse it and display the results on the HTML page. The pre-requisite of the HTML page is the javascript file which can be downloaded from the Downloads section.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <link href="https://examples.javacodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9zdGFja3BhdGguYm9vdHN0cmFwY2RuLmNvbS8=bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
    <title>Index</title>
</head>
<style type="text/css">
    .container-width {
        max-width: 1850px !important;
    }

    .padding12 {
        padding: 12px;
    }

    table, th, td {
        text-align: center;
    }
</style>
<body>

<div class="container container-width">
    <div class="row">
        <div class="padding12">
            <h3>Spring Webflux Example</h3>
            <small>Stream of data which will simulate every 2 seconds.</small>
        </div>
        <table class="table table-bordered" id="employees">
            <thead>
            <tr>
                <th>Id</th>
                <th>First Name</th>
                <th>Last Name</th>
                <th>Gender</th>
                <th>Email address</th>
                <th>Phone number</th>
                <th>Joining date and time</th>
            </tr>
            </thead>
            <tbody>
            <tr data-th-each="employee : ${employees}">
                <td data-th-text="${employee.id}"></td>
                <td data-th-text="${employee.firstName}"></td>
                <td data-th-text="${employee.lastName}"></td>
                <td data-th-text="${employee.gender}"></td>
                <td data-th-text="${employee.emailAddress}"></td>
                <td data-th-text="${employee.phoneNumber}"></td>
                <td data-th-text="${employee.doj}"></td>
            </tr>
            </tbody>
        </table>
    </div>
</div>

<script data-th-src="@{/js/main.js}"></script>

</body>

</html>

4. Run the Application

To execute the application, right-click on the SpringbootWebfluxApplication.java class, Run As -> Java Application.

spring webflux-run the app
Fig. 2: Run the Application

5. Project Demo

When the application is started, open the browser of your choice and hit the following url – http://localhost:9090/api/stream/employees to verify the non-blocking response feature of the webflux. The results will appear one by one after a delay of 2 seconds in an event form. If you would like to view the results beautifully hit the following url – http://localhost:9090/. The latter one will open the HTML page and display the stream of employee events. That is all for this tutorial and I hope the article served you whatever you were looking for. Happy Learning and do not forget to share!

6. Summary

In this section, you learned:

  • Introduction to Webflux, Reactive programming, Spring Webflux, and Lombok
  • Steps to implement spring webflux in a spring boot application

You can download the sample application as an Eclipse project in the Downloads section.

7. Download the Project

This was an example to introduce spring webflux in a spring boot application.

Download
You can download the full source code of this example here: Introduction to Spring Webflux

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Wei He
Wei He
3 years ago

In order to receive the event update from the server every two seconds, must the client hold the HTTP-connection to the server forever?

Back to top button