Spring Web Reactive | 1. Spring WebFlux | 1.1. Overview

Why was Spring WebFlux created?

One reason was the need for a non-blocking web stack that could handle concurrency with fewer threads and scale with fewer hardware resources. Servlet 3.1 introduced non-blocking I/O APIs, but using them moves away from Servlet APIs whose contracts are synchronous (Filter, Servlet) or blocking (getParameter, getPart). This motivated a new common API that could serve as a foundation across non-blocking runtimes such as Netty.

Another reason was functional programming. Annotations in Java 5 enabled annotated REST controllers and unit tests, while lambdas in Java 8 opened the door to functional APIs. This is useful for non-blocking applications and continuation-style APIs such as CompletableFuture and ReactiveX, which declaratively compose asynchronous logic. Spring WebFlux can therefore provide functional web endpoints alongside annotated controllers.

1.1.1. Definition of Reactive

The term reactive describes a programming model built around reacting to change. Network components react to I/O events, and UI controllers react to mouse events. Non-blocking code is reactive because it reacts to notifications that work has completed or data is available instead of blocking.

Another important mechanism is non-blocking back pressure. In synchronous imperative code, blocking calls naturally make callers wait. In non-blocking code, event rates must be controlled so that fast producers do not overwhelm consumers.

Reactive Streams defines a small specification, also adopted in Java 9, for asynchronous components with back pressure. For example, a data repository acting as a Publisher can produce data that an HTTP server acting as a Subscriber writes to a response. Its main purpose is to let subscribers control how quickly publishers produce data.

FAQ: What if a publisher cannot slow down?
Reactive Streams defines the mechanism and boundaries only. You must decide whether to buffer, drop, or fail.

1.1.2. Reactive API

Reactive Streams is important for interoperability, but it is too low-level for application APIs. Applications need richer APIs for composing asynchronous logic, similar to the Java 8 Stream API but not limited to collections.

Reactor is the reactive library used by Spring WebFlux. Its Mono and Flux types operate on 0..1 and 0..N sequences with operators aligned with the ReactiveX operator vocabulary. Reactor supports non-blocking back pressure and focuses on server-side Java.

WebFlux requires Reactor internally but interoperates with other reactive libraries through Reactive Streams. It accepts a generic Publisher as input, adapts it to Reactor types internally, and returns Flux or Mono. Where possible, such as annotated controllers, WebFlux transparently adapts RxJava and other reactive libraries. See Reactive Libraries.

WebFlux also supports Kotlin Coroutines for a more imperative style. See the Coroutines API.

1.1.3. Programming Models

The spring-web module provides HTTP abstractions, Reactive Streams adapters, codecs, and the core WebHandler API. Spring WebFlux offers two programming models:

  • Annotated controllers: Consistent with Spring MVC and based on the same annotations. Both MVC and WebFlux controllers support reactive return types, while WebFlux also supports reactive @RequestBody arguments.
  • Functional endpoints: A lightweight lambda-based model. Applications handle request routing and processing directly instead of declaring intent with annotations and callbacks.

1.1.4. Applicability

Spring MVC and WebFlux can be used together and are designed for continuity and consistency.

spring-mvc-and-webflux-venn

Consider the following:

  • Keep a working Spring MVC application unless there is a reason to change it. Imperative code is easier to write, understand, and debug, and most libraries are blocking.
  • WebFlux is suitable when you already need a non-blocking stack. It supports Netty, Tomcat, Jetty, Undertow, and Servlet 3.1+ containers, multiple programming models, and reactive libraries such as Reactor and RxJava.
  • Functional WebFlux endpoints are useful for lightweight Java 8 lambda or Kotlin web frameworks, especially small applications and microservices that benefit from transparency and control.
  • Microservice architectures can mix MVC controllers, WebFlux controllers, and functional endpoints while reusing knowledge of the shared annotation model.
  • If your application depends on blocking persistence APIs such as JPA or JDBC, or blocking network APIs, MVC is usually a better fit. Moving blocking calls to other threads is possible but does not fully use a non-blocking stack.
  • MVC applications that call remote services can still use reactive WebClient and return Reactor, RxJava, or other reactive types from controller methods.
  • Teams should account for the learning curve of non-blocking, functional, and declarative programming. A practical first step is using reactive WebClient, starting small, and measuring the benefit.

1.1.5. Servers

Spring WebFlux supports Tomcat, Jetty, Servlet 3.1+ containers, Netty, and Undertow through a common low-level API.

Spring Framework does not directly start or stop servers, but applications can be assembled and run with a few lines of code. Spring Boot automates these steps and defaults to Netty because it is widely used for asynchronous non-blocking workloads and lets clients and servers share resources.

Tomcat and Jetty support both MVC and WebFlux, but MVC relies on blocking Servlet I/O while WebFlux uses Servlet 3.1 non-blocking I/O behind an adapter. WebFlux uses the Undertow API directly without the Servlet API.

1.1.6. Performance

Reactive non-blocking applications are not generally faster. They can require more work and slightly increase processing time. Their main benefit is scalability with a small fixed number of threads and less memory, which improves resilience under load. This advantage is most visible when workloads include latency and unpredictable network I/O.

1.1.7. Concurrency Model

MVC assumes applications may block the current thread, so Servlet containers use large thread pools. WebFlux assumes applications do not block, so non-blocking servers use a small fixed-size pool of event-loop workers.

Scaling with fewer threads works because non-blocking applications rely on callbacks instead of requiring extra threads to absorb blocking calls.

Calling Blocking APIs

Reactor and RxJava provide the publishOn operator to continue processing on another thread. This offers an escape hatch, but blocking APIs do not fit the concurrency model.

Mutable State

Reactor and RxJava operators form pipelines whose stages process data sequentially. Application code in a pipeline is not invoked concurrently, reducing the need to protect mutable state.

Threading Model

  • A basic WebFlux server generally has one server thread and a few request-processing threads, often matching CPU cores. Servlet containers may start with more threads because they support both blocking and non-blocking I/O.
  • Reactive WebClient uses an event-loop model with a fixed number of threads such as reactor-http-nio-. Reactor Netty clients and servers share event-loop resources by default.
  • Reactor and RxJava schedulers abstract thread pools. Operators such as publishOn switch processing to strategies such as parallel for CPU-bound work or elastic for I/O-bound work.
  • Data-access libraries and other dependencies may create their own threads.

Configuration

Spring Framework does not start or stop servers. Configure the server with server-specific APIs or Spring Boot options. Configure WebClient directly with its builder, and consult each library’s documentation for other components.