As applications scale and workloads increase, traditional thread-per-request models often hit limitations. Reactive programming offers a non-blocking, event-driven alternative that makes better use of system resources under high load.

Spring WebFlux, introduced in Spring 5, is a reactive-stack web framework built to support non-blocking I/O. It works on top of Project Reactor, enabling Java developers to build reactive microservices that are highly scalable and efficient.

This guide walks you through the foundations of reactive programming and how to build reactive microservices using Spring Boot and WebFlux.


What is Reactive Programming?

Reactive programming is a paradigm focused on asynchronous data streams and event-driven architecture. Instead of blocking threads, reactive systems respond to data events as they occur.

Core principles:

  • Responsive: Handle requests in a timely manner
  • Resilient: Stay responsive in the face of failure
  • Elastic: Scale to handle varying load
  • Message-driven: Communicate asynchronously

Introducing Spring WebFlux

Spring WebFlux supports two programming models:

  1. Annotated Controllers (like @RestController)
  2. Functional Endpoints (with RouterFunction and HandlerFunction)

Unlike Spring MVC, WebFlux is built on non-blocking runtimes like Netty, making it ideal for reactive applications.

Add dependencies to pom.xml:

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

Working with Reactive Types

Spring WebFlux uses Mono and Flux from Project Reactor:

  • Mono: 0 or 1 value
  • Flux: 0 to many values

Examples:

@GetMapping("/mono")
public Mono<String> getMono() {
return Mono.just("Hello Reactive World");
}

@GetMapping("/flux")
public Flux<Integer> getFlux() {
return Flux.range(1, 5);
}

These types represent non-blocking, asynchronous streams.


Reactive Repository with Spring Data

Spring Data R2DBC enables reactive access to relational databases.

Add dependencies:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
</dependency>

Define a reactive repository:

public interface UserRepository extends ReactiveCrudRepository<User, Long> {
Flux<User> findByRole(String role);
}

Everything from queries to saving records is non-blocking.


Functional Routing Example

Functional endpoints offer more control and are fully reactive:

@Bean
public RouterFunction<ServerResponse> route(UserHandler handler) {
return RouterFunctions
.route(GET("/users"), handler::getAllUsers)
.andRoute(POST("/users"), handler::createUser);
}

Handler implementation:

public Mono<ServerResponse> getAllUsers(ServerRequest request) {
return ServerResponse.ok().body(userRepository.findAll(), User.class);
}

Error Handling in WebFlux

Use onErrorResume or global exception handling with @ControllerAdvice:

@GetMapping("/user/{id}")
public Mono<ResponseEntity<User>> getUser(@PathVariable Long id) {
return userRepository.findById(id)
.map(ResponseEntity::ok)
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
}

For global error handling, implement ErrorWebExceptionHandler.


WebClient for Reactive HTTP Calls

Replace RestTemplate with WebClient for non-blocking HTTP calls:

WebClient client = WebClient.create("http://localhost:8080");

Mono<User> userMono = client.get()
.uri("/users/1")
.retrieve()
.bodyToMono(User.class);

WebClient integrates seamlessly with reactive flows.


Testing Reactive APIs

Use StepVerifier from Reactor Test to test Mono and Flux:

@Test
void testMono() {
Mono<String> mono = Mono.just("Spring");

    StepVerifier.create(mono)
        .expectNext("Spring")
        .verifyComplete();
}

For controller tests, use WebTestClient to simulate HTTP requests:

@Autowired
private WebTestClient webTestClient;

@Test
void testGetUsers() {
webTestClient.get()
.uri("/users")
.exchange()
.expectStatus().isOk()
.expectBodyList(User.class);
}

Benefits of Building Reactive Microservices

  • Better resource utilization under high load
  • Improved throughput with fewer threads
  • Suitable for event-driven and real-time systems
  • Seamless integration with Kafka, MongoDB, R2DBC, and more

Use cases:

  • Chat systems
  • Live feeds and dashboards
  • API gateways and aggregators
  • High-concurrency services

Best Practices

  • Use reactive APIs end-to-end (database, HTTP, messaging)
  • Avoid blocking calls (like JDBC, Thread.sleep)
  • Use Schedulers.boundedElastic() for bridging non-reactive tasks
  • Limit response sizes and apply backpressure mechanisms
  • Monitor latency and throughput using tools like Micrometer

Conclusion

Reactive programming with Spring WebFlux enables you to build microservices that are lightweight, scalable, and resilient under load. By embracing Mono, Flux, and non-blocking patterns, you unlock the full power of modern Java applications built for real-time performance.

Whether you’re scaling APIs or building event-driven pipelines, Spring Boot + WebFlux provides the ideal foundation for reactive microservice architecture.