10 WebFlux Interview Questions and Answers
Prepare for your next interview with our comprehensive guide on WebFlux, covering core concepts and practical applications in reactive web development.
Prepare for your next interview with our comprehensive guide on WebFlux, covering core concepts and practical applications in reactive web development.
WebFlux is a reactive web framework in the Spring ecosystem, designed to handle asynchronous and non-blocking operations with ease. It leverages the power of Project Reactor to provide a more efficient way to manage high-throughput and low-latency web applications. WebFlux is particularly well-suited for modern microservices architectures and real-time data streaming applications, making it a valuable skill for developers working in these areas.
This article offers a curated selection of WebFlux interview questions and answers to help you prepare effectively. By familiarizing yourself with these questions, you will gain a deeper understanding of WebFlux’s core concepts and practical applications, enhancing your ability to tackle technical challenges in your upcoming interviews.
Mono
and Flux
.In WebFlux, Mono
and Flux
are the primary reactive types provided by the Reactor library for handling asynchronous data sequences.
Mono
represents a single or empty result, suitable for zero or one item emissions.Flux
represents a sequence of 0 to N items, used for multiple item emissions.These types are fundamental for building non-blocking, reactive applications, allowing for the composition of asynchronous logic in a declarative manner.
Example:
import reactor.core.publisher.Mono; import reactor.core.publisher.Flux; public class WebFluxExample { public static void main(String[] args) { Mono<String> mono = Mono.just("Hello, Mono!"); mono.subscribe(System.out::println); Flux<String> flux = Flux.just("Hello", "Flux", "World"); flux.subscribe(System.out::println); } }
Backpressure in WebFlux can be managed using strategies from the Reactive Streams specification, such as onBackpressureBuffer
, onBackpressureDrop
, or onBackpressureLatest
.
Example:
Flux<Integer> flux = Flux.range(1, 100) .onBackpressureBuffer(10, i -> System.out.println("Dropped: " + i)) .doOnNext(i -> { try { Thread.sleep(50); // Simulate slow consumer } catch (InterruptedException e) { e.printStackTrace(); } }); flux.subscribe(System.out::println);
Here, onBackpressureBuffer
buffers up to 10 items and drops any additional ones, printing a message when an item is dropped. This manages the data consumption rate and prevents resource exhaustion.
flatMap
and map
in the context of Mono
and Flux
?In WebFlux, map
and flatMap
transform sequences differently.
Mono
or Flux
with transformed elements.Mono
or Flux
, flattening the resulting sequences.Example:
import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public class WebFluxExample { public static void main(String[] args) { // Using map Flux<Integer> flux = Flux.just(1, 2, 3) .map(i -> i * 2); flux.subscribe(System.out::println); // Outputs: 2, 4, 6 // Using flatMap Flux<Integer> flatMappedFlux = Flux.just(1, 2, 3) .flatMap(i -> Mono.just(i * 2)); flatMappedFlux.subscribe(System.out::println); // Outputs: 2, 4, 6 } }
map
applies a synchronous transformation, while flatMap
applies an asynchronous one, flattening the results into a single Flux
.
Error handling in a WebFlux stream can be done using operators like onErrorResume
, onErrorReturn
, and doOnError
. These operators define fallback logic, return default values, or perform side effects when an error occurs.
Example:
import reactor.core.publisher.Flux; public class ErrorHandlingExample { public static void main(String[] args) { Flux<String> flux = Flux.just("1", "2", "a", "3") .map(Integer::parseInt) .onErrorResume(e -> { System.out.println("Error occurred: " + e.getMessage()); return Flux.just(-1); }); flux.subscribe(System.out.println); } }
Here, onErrorResume
handles any NumberFormatException
by emitting a fallback value of -1
and printing the error message.
Schedulers
and how you would use them.Schedulers in WebFlux control the execution context of reactive streams, specifying which thread or thread pool should execute a part of the pipeline. This optimizes performance and ensures that blocking operations do not interfere with non-blocking ones.
Example:
import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; public class SchedulerExample { public static void main(String[] args) { Mono.fromCallable(() -> { // Simulate a blocking operation Thread.sleep(1000); return "Result"; }) .subscribeOn(Schedulers.boundedElastic()) .doOnNext(result -> System.out.println("Received: " + result)) .block(); } }
In this example, subscribeOn(Schedulers.boundedElastic())
runs the blocking operation on a separate thread pool, avoiding blocking the main event loop.
WebTestClient
?WebTestClient
is a non-blocking, reactive client for testing WebFlux applications, simulating HTTP requests and verifying responses.
Example:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; import org.springframework.test.web.reactive.server.WebTestClient; @WebFluxTest public class MyControllerTest { @Autowired private WebTestClient webTestClient; @Test public void testGetEndpoint() { webTestClient.get().uri("/my-endpoint") .exchange() .expectStatus().isOk() .expectBody(String.class).isEqualTo("Expected Response"); } }
WebTestClient
sends a GET request to /my-endpoint
, verifying the status and response body.
Custom operators in WebFlux allow developers to define their own reactive transformations and operations, useful for specific business logic or performance optimizations.
Example:
import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.Operators; import java.util.function.Function; public class CustomOperatorExample { public static void main(String[] args) { Flux<Integer> flux = Flux.just(1, 2, 3, 4, 5) .transform(applyCustomOperator()); flux.subscribe(System.out::println); } private static Function<Flux<Integer>, Flux<Integer>> applyCustomOperator() { return flux -> flux.map(i -> i * 2); } }
The applyCustomOperator
method defines a custom operator that doubles each element in the stream, applied using the transform
method.
Server-Sent Events (SSE) enable a server to push real-time updates to the client over a single HTTP connection. In WebFlux, you can implement an SSE endpoint using the Flux
class.
Example:
import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import java.time.Duration; import java.time.LocalTime; @RestController public class SSEController { @GetMapping(value = "/stream-sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> streamEvents() { return Flux.interval(Duration.ofSeconds(1)) .map(sequence -> "SSE - " + LocalTime.now().toString()); } }
The streamEvents
method returns a Flux
emitting a new event every second, with the endpoint producing text/event-stream
media type.
WebFlux supports reactive programming principles by leveraging Project Reactor, which provides Mono
and Flux
for non-blocking operations and backpressure. It uses a non-blocking, event-driven model to handle requests and responses, processing many concurrent requests without being constrained by thread count.
Example:
@GetMapping("/hello") public Mono<String> sayHello() { return Mono.just("Hello, World!"); }
The sayHello
method returns a Mono
emitting “Hello, World!”, demonstrating non-blocking asynchronous operations.
Context
in Project Reactor and how it can be used.In Project Reactor, Context
is a key-value store associated with a reactive sequence, allowing the passage of contextual information downstream without modifying data flow. It is immutable, ensuring thread safety and consistency.
Example:
import reactor.core.publisher.Mono; import reactor.util.context.Context; public class ContextExample { public static void main(String[] args) { Mono<String> mono = Mono.just("Hello") .flatMap(value -> Mono.deferContextual(ctx -> { String user = ctx.get("user"); return Mono.just(value + ", " + user); })) .contextWrite(Context.of("user", "John")); mono.subscribe(System.out::println); // Output: Hello, John } }
A Mono
is created with a string value, and flatMap
accesses the Context
to retrieve the “user” value. The contextWrite
method adds the key-value pair to the Context
.