skip to content

What is Spring's WebClient and why is it the recommended replacement for RestTemplate?

level: juniorimportance: must knowfreq 78%

answer

  1. non-blocking reactive HTTP client
  2. returns Mono/Flux not values
  3. RestTemplate = maintenance mode
  4. builder().baseUrl().build()
  5. lazy until subscribed

basics

~20 s

WebClient is Spring's non-blocking, reactive HTTP client. Unlike RestTemplate, which blocks a thread per request, WebClient uses async I/O and returns Mono/Flux, so a few threads handle many concurrent calls. RestTemplate is in maintenance mode.

solid answer

~40 s

WebClient is the reactive HTTP client in spring-webflux, built on Reactor Netty by default. It is non-blocking: instead of returning a value and holding a thread until the response arrives (like RestTemplate), it returns a Mono<T> or Flux<T> that emits when data is ready, freeing the event-loop thread to serve other requests. You build one with WebClient.builder().baseUrl(...).build(), then chain get()/post(), retrieve(), and bodyToMono(Type.class). Spring officially puts RestTemplate in maintenance mode (no new features) and recommends WebClient even in blocking apps, where you can call .block() if needed. Its strengths: high concurrency with few threads, a fluent functional API, streaming with Flux, and first-class error/retry operators.

code

java · 24 lines
java
@Configuration
class ClientConfig {
    @Bean
    WebClient apiClient(WebClient.Builder builder) {
        // Reuse a single builder-configured client app-wide
        return builder
            .baseUrl("https://api.example.com")
            .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
            .build();
    }
}

@Service
class UserService {
    private final WebClient client;
    UserService(WebClient apiClient) { this.client = apiClient; }

    Mono<User> findUser(long id) {
        return client.get()
            .uri("/users/{id}", id)
            .retrieve()
            .bodyToMono(User.class);
    }
}

go deeper

for a junior

Know it is the non-blocking replacement for RestTemplate and returns Mono/Flux.

for a middle

Explain the thread/blocking difference and the builder().baseUrl().build() lifecycle; reuse one instance.

for a senior

Discuss laziness/subscription, event-loop threads, and safe use inside blocking apps.

for a principal

Weigh when non-blocking actually pays off vs. added complexity; connection-pool tuning and back-pressure implications.

## What WebClient is `WebClient` is the HTTP client shipped with **`spring-webflux`** (package `org.springframework.web.reactive.function.client`). It is **reactive and non-blocking**: every call returns a Reactor publisher — a `Mono<T>` (0..1 result) or `Flux<T>` (0..N results) — rather than a materialized value. ## Blocking vs non-blocking (the core distinction) - **`RestTemplate`** (from `spring-web`) is *synchronous/blocking*. When you call `restTemplate.getForObject(...)`, the calling thread is parked, doing nothing, until the remote server responds. Under load you need one thread per in-flight request; thousands of concurrent calls need thousands of threads, which costs memory and context-switching. - **`WebClient`** is *asynchronous/non-blocking*. It uses an event loop (Reactor Netty by default). The thread that starts the request is released immediately; when bytes arrive from the network, a callback resumes processing. A handful of event-loop threads can multiplex thousands of concurrent requests. ## Anatomy of a call ```java WebClient client = WebClient.builder() .baseUrl("https://api.example.com") // prefix for all requests .build(); Mono<User> user = client.get() .uri("/users/{id}", 42) // resolved against baseUrl .retrieve() // trigger + default error handling .bodyToMono(User.class); // decode body into a User ``` Key builder pieces: - **`WebClient.builder()`** — returns a `WebClient.Builder` for shared config. - **`.baseUrl(String)`** — a common prefix applied to every relative `uri(...)`. Absolute URIs override it. - **`.build()`** — produces an immutable, thread-safe `WebClient`. Create one and reuse it (do not build per request). ## Why Spring recommends it over RestTemplate The RestTemplate Javadoc and Spring docs state it is **in maintenance mode** — it still works and is supported, but no new features are added; WebClient is the modern successor. WebClient wins on: high concurrency with few threads, streaming (`Flux`), a fluent functional API, and built-in error/retry/timeout operators. In a **blocking (Spring MVC) app** you can still use WebClient and call `.block()` to get the value synchronously — you just lose the non-blocking benefit for that call. ## Gotchas - **Nothing happens until you subscribe.** `retrieve()` alone does not send the request; the `Mono`/`Flux` is lazy. Something must subscribe (the WebFlux framework does this when you return it from a controller; in tests you `block()`). - Reuse a single `WebClient`/`Builder`; building per call wastes the underlying connection pool. - Do not call `.block()` on a WebFlux event-loop thread — it can deadlock the loop.

  • Can you use WebClient in a traditional blocking Spring MVC app?
    Yes. Add spring-webflux to the classpath and use WebClient; call .block() to get the value synchronously. You lose non-blocking concurrency for that call but gain the modern API and retry/timeout operators. Just never call .block() on a WebFlux event-loop thread.
  • Does calling retrieve() send the HTTP request immediately?
    No. The returned Mono/Flux is lazy — the request fires only when something subscribes. In a WebFlux controller the framework subscribes; in tests you call block() or use StepVerifier.

saying these in an interview costs you the question

  • Saying RestTemplate is deprecated/removed (it's maintenance mode, still supported)
  • Claiming retrieve() sends the request eagerly
  • Thinking WebClient needs a full WebFlux (reactive) app to be used
  • Building a new WebClient per request

context