middle
Как работает WebClient и чем он отличается от RestTemplate?
WebClient — неблокирующий, реактивный HTTP-клиент из Spring WebFlux, пришедший на замену блокирующему RestTemplate. Начиная со Spring 6.1, появился также RestClient — блокирующий клиент с fluent API.
Эволюция HTTP-клиентов в Spring
| Клиент | Версия Spring | Модель | Статус в 2026 |
|---|---|---|---|
| RestTemplate | Spring 3 (2009) | Блокирующий | Maintenance mode |
| WebClient | Spring 5 (2017) | Реактивный (неблокирующий) | Актуален |
| RestClient | Spring 6.1 (2023) | Блокирующий, fluent API | Рекомендуемый для блокирующего кода |
WebClient — базовое использование
Пример
WebClient webClient = WebClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
// GET — Mono
Mono<User> user = webClient.get()
.uri("/users/{id}", 1)
.retrieve()
.bodyToMono(User.class);
// GET — Flux (список)
Flux<User> users = webClient.get()
.uri("/users")
.retrieve()
.bodyToFlux(User.class);
// POST
Mono<User> created = webClient.post()
.uri("/users")
.bodyValue(new CreateUserRequest("John", "john@example.com"))
.retrieve()
.bodyToMono(User.class);
Обработка ошибок HTTP
Mono<User> user = webClient.get()
.uri("/users/{id}", userId)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, response ->
response.bodyToMono(ErrorResponse.class)
.flatMap(error -> Mono.error(new ApiException(error.getMessage()))))
.onStatus(HttpStatusCode::is5xxServerError, response ->
Mono.error(new ServiceUnavailableException("Сервис недоступен")))
.bodyToMono(User.class)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1))
.filter(e -> e instanceof ServiceUnavailableException));
Таймауты и конфигурация
HttpClient httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(5))
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000);
WebClient webClient = WebClient.builder()
.baseUrl("https://api.example.com")
.clientConnector(new ReactorClientHttpConnector(httpClient))
.filter(ExchangeFilterFunction.ofRequestProcessor(request -> {
log.info("Request: {} {}", request.method(), request.url());
return Mono.just(request);
}))
.build();
RestClient (Spring 6.1+) — блокирующая альтернатива
Пример
RestClient restClient = RestClient.builder()
.baseUrl("https://api.example.com")
.build();
User user = restClient.get()
.uri("/users/{id}", 1)
.retrieve()
.body(User.class);
Сравнение
| Критерий | RestTemplate | WebClient | RestClient |
|---|---|---|---|
| Блокирующий | Да | Нет | Да |
| API стиль | Методы (getForObject) | Fluent chain | Fluent chain |
| Streaming | Нет | Да | Нет |
| Зависимость | spring-web | spring-webflux + reactor-netty | spring-web |
| Тестирование | MockRestServiceServer | MockWebServer | MockRestServiceServer |
Частые ошибки
- Создавать новый WebClient на каждый запрос — WebClient потокобезопасный, создавайте один раз
- Вызывать
.block()в реактивном контексте — deadlock; допустим только в блокирующем коде - Не обрабатывать HTTP-ошибки — без
onStatusWebClient бросает неинформативное исключение - Использовать WebClient только потому, что RestTemplate deprecated — для блокирующего кода лучше RestClient
Как используется в 2026
- RestClient — стандартный HTTP-клиент для Spring MVC (Spring 6.1+)
- WebClient — для реактивных приложений (WebFlux) и streaming
- RestTemplate встречается в legacy-коде
- Spring HTTP Interface (
@HttpExchange) работает и с WebClient, и с RestClient — декларативный стиль
На собеседовании: знайте три клиента (RestTemplate, WebClient, RestClient) и когда какой использовать. Частая ошибка — говорить «RestTemplate deprecated, используйте WebClient» без нюансов. RestTemplate в maintenance mode, но для блокирующего кода лучше RestClient, а не WebClient с .block().