Reactive Programming In Spring Framework

This article is the third of a series on Reactive Programming. If you have not read the previous article, I would strongly advise you to do so.

In this article, we will discuss how Reactive Programming is made possible in Spring.

Spring WebFlux

Spring WebFlux is the Spring project that makes Reactive Programming possible in the Spring Framework. It is a parallel version of Spring MVC and supports fully non-blocking reactive streams. It supports the back-pressure concept and uses Netty as an inbuilt server to run reactive applications.

Spring Webflux uses Projector Reactor as reactive Library. Reactor is a Reactive Streams library that was developed in close collaboration with Spring.

Publishers and Subscribers in Spring WebFlux

Webflux provides two categories of publishers: Cold and Hot publishers. Cold Publishers are publishers which start publishing from the beginning of the stream for all Subscribers which subscribe to it. Hot Publishers are those which do not start publishing from the beginning. They publish continuously until they do not have any more elements. Any subscriber will receive elements that the publisher makes available at the moment it subscribes.

To subscribe to a publisher, you just need to pass a reference to a Consumer method to the subscribe method of the Publisher.

Spring WebFlux provides two Publisher Classes: Flux and Mono. The Flux Publisher class can publish 0 or more elements. There is no limit to the number of elements it can publish. The Mono Publisher class on the other hand can at most one element.

Hands-on-Code

For this tutorial, I will use Maven as Dependency Management Tool. Here is the pom.xml configuration

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         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.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.learnreactivespring</groupId>
    <artifactId>item-client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>WebFlux Test</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

Constructing a Cold Publisher and Subscribing to It

In the following code, I will construct a publisher which outputs a sequence of letters. Then, I will attach two subscribers to it. The first one will print each of the letters and the second one will print the ASCII code of the letters.

public class ColdPublisherTest {
    public static void main(String[] args) throws InterruptedException {
        Flux<String> stringFlux = Flux.just("A", "B", "C", "D", "E", "F")
                .delayElements(Duration.ofSeconds(1));
        stringFlux.subscribe(s -> System.out.println("Subscriber 1: " + s));
        Thread.sleep(2000);
        stringFlux.subscribe(s -> System.out.println("Subscriber 2: " + ((int) s.charAt(0))));
        Thread.sleep(7000);
    }
}

The just() method takes a sequence of elements of the same type and publishes them. The delay method makes the publisher publish elements at a particular rate. Since the subscribers are working on different threads, Thread.sleep() is required at the end of the main thread so that we can see all the output of the subscribers. Below is the Output:

Subscriber 1: A
Subscriber 1: B
Subscriber 2: 65
Subscriber 1: C
Subscriber 2: 66
Subscriber 1: D
Subscriber 2: 67
Subscriber 1: E
Subscriber 2: 68
Subscriber 1: F
Subscriber 2: 69
Subscriber 2: 70

From the output, you can see that both subscribers receive the same sequence of characters from the publisher even though they subscribed at different times. This is proof that the publisher is a cold one.

Constructing a Hot Publisher and Subscribing to It

In the following code, I will construct a publisher which outputs a sequence of letters. Then, I will attach two subscribers to it. Both of the Subscribers will print the letters published to them.

public class HotPublisherTest {
    public static void main(String[] args) throws InterruptedException {
        Flux<String> stringFlux = Flux.just("A", "B", "C", "D", "E", "F")
                .delayElements(Duration.ofSeconds(1)).log();

        //Creating a Hot Publisher
        final ConnectableFlux<String> connectableFlux = stringFlux.publish();
        connectableFlux.connect();// to make it behave as a hot publisher
        connectableFlux.subscribe(s -> System.out.println("Subscriber 1 : " + s));
        Thread.sleep(3000);

        connectableFlux.subscribe(s -> System.out.println("Subscriber 2 : " + s));// does not emits value from beginning

        Thread.sleep(4000);
    }
}

Here is the Output:

Subscriber 1 : A
Subscriber 1 : B
Subscriber 1 : C
Subscriber 2 : C
Subscriber 1 : D
Subscriber 2 : D
Subscriber 1 : E
Subscriber 2 : E
Subscriber 1 : F
Subscriber 2 : F

Since the two subscribers have subscribed at different times, the latter will print fewer letters than the former. This is because at the time it subscribed to the publisher, some letters had already been published. This is proof that the publisher is a hot one.

Conclusion

This marks the end of this tutorial. Hope it has been beneficial to you. In the next one, we will look at some basic transformation methods provided in Spring Webflux.

Leave a Reply

Your email address will not be published. Required fields are marked *