blog bg

May 27, 2025

Implementing Reactive Programming in Java with Project Reactor

Share what you learn in this blog to prepare for your interview, create your forever-free profile now, and explore how to monetize your valuable knowledge.

 

Blocking operations occur when you wait for a job to complete before going on. Turn that idea on its head and imagine processes working together in a smooth, non-blocking flow. Reactive programming is simpler than ever to create in Java using Project Reactor. Reactive programming with Project Reactor may help you create non-blocking, event-driven applications that scale smoothly. 

 

What is Project Reactor? 

Project Reactor is a staple of reactive programming in modern Java development. It follows the Reactive Streams specification for asynchronous stream processing with non-blocking backpressure. Project Reactor lets your apps manage enormous amounts of data without blocking, making them more efficient and scalable. Flux and Mono, two Project Reactor abstractions, provide declarative data stream manipulation without callbacks or thread management. 

 

Setting Up a Simple Reactive Stream

Start with a basic example. I will explain how to make a dynamic stream that produces a series of numbers. We'll use Flux for our stream, which can release 0 to N things. 

Here's an easy code example that shows how to create a Flux stream that sends out numbers from 1 to 5: 

 

import reactor.core.publisher.Flux;

public class ReactiveExample {
    public static void main(String[] args) {
       Flux<Integer> numberStream = Flux.just(1, 2, 3, 4, 5);
       numberStream.subscribe(System.out::println);
    }
}

This prints 1 to 5 to the console. See how simple it is to create a dynamic stream, there is no need for complicated thread management. The subscribe() method lets you connect to the stream and handle each thing as it comes in. 

 

Transforming Data with Operators 

After you have a set of data, you can start changing it. Project Reactor offers several operators to change your data in a clean, useful way. Two of the most widely used tools are map and filter. 

Let's change the stream from the last case. We will use map to increase the numbers by 2 and filter to only keep the even numbers: 

 

Flux<Integer> transformedStream = Flux.just(1, 2, 3, 4, 5)
                                      .map(num -> num * 2)
                                     .filter(num -> num % 2 == 0);

transformedStream.subscribe(System.out::println);

We are taking each number in the series, doubling it, and then filtering any odd numbers. The output will be:

 

4
8

This shows the strength of functional reactive programming. It lets you connect tasks smoothly and clearly, changing your data as it moves through the stream. 

 

Error Handling in Reactive Streams 

A common problem in reactive programming is dealing with mistakes in a smooth way. In regular blocking applications, errors are usually handled using try-catch blocks. However, in reactive streams, we have to manage errors in a way that doesn't disrupt the program's flow and can happen at different times. 

Project Reactor makes it easy to handle errors using operators like onErrorResume. If our stream has an error, we want to give a fallback value. 

 

Flux<Integer> safeStream = Flux.just(1, 2, 3)
                               .map(num -> {
                                   if (num == 2) throw new RuntimeException("Error on 2");
                                   return num;
                               })
                               .onErrorResume(e -> Flux.just(10, 20, 30));

safeStream.subscribe(System.out::println);

If there's an error while handling the number 2, the stream will then use a set of numbers that is predefined: 10, 20, and 30. This lets the stream keep running without stopping, which helps it handle problems better. 

 

Managing Backpressure 

When creating dynamic systems, an important issue to consider is backpressure. This occurs when data is created quicker than it can be used, which may overload the system. Project Reactor has tools to handle this problem. 

Let's look at how we can handle backpressure. Usually, we use onBackpressureBuffer to hold data for a while when the consumer cannot process it quickly enough. 

 

Flux<Integer> backpressureStream = Flux.range(1, 1000)
                                      .onBackpressureBuffer(100, i -> System.out.println("Buffer overflow"));

backpressureStream.subscribe(System.out::println);

We have a 100-item buffer here. Overflowing buffers log messages. This protects users from too much info. 

 

Conclusion 

You should now understand how to use Project Reactor for reactive programming in Java. You have built up a reactive stream, transformed data using operators, handled failures, and managed backpressure. With these methods, you can develop non-blocking, scalable apps that handle huge volumes of data effectively without losing speed. For reactive programming in microservices or real-time apps, Project Reactor is a powerful toolbox.

165 views

Please Login to create a Question