RestClient en Spring Boot 4: La Modernización de la Comunicación Síncrona
La llegada de Spring Boot 4.0, cimentado sobre Spring Framework 7, marca un cambio de paradigma crucial en la forma en que las aplicaciones Java gestionan las comunicaciones HTTP. El foco se centra en adoptar arquitecturas modernas, eliminar la deuda técnica legada de RestTemplate, y aprovechar al máximo las capacidades de concurrencia de Java 21+.
Novedades y Mejoras Clave
RestClient fue introducido en Spring Framework 6.1 (y Spring Boot 3.2) como el sucesor moderno de RestTemplate, ofreciendo una alternativa síncrona y flexible. En Spring Boot 4, esta herramienta se consolida como el estándar para el desarrollo síncrono.
Nueva Abstracción del Cliente Síncrono
RestClient es un cliente HTTP síncrono que proporciona una API fluida y basada en builders para enviar solicitudes HTTP. Está diseñado para ser el cliente predeterminado en aplicaciones que utilizan el stack tradicional de Spring MVC.
| Característica | Descripción |
|---|---|
| Diseño Fluent API | Permite construir peticiones usando encadenamiento de métodos, imitando la sintaxis de WebClient, lo que mejora la legibilidad y reduce el código repetitivo (boilerplate). |
| Manejo de Errores Dedicado | Soporte integrado para manejar errores basados en códigos de estado HTTP (4xx y 5xx) utilizando el método onStatus(). |
| Customización Simplificada | Facilidad para añadir encabezados, query parameters y variables de ruta directamente a través de los métodos de la API. |
| Interceptors y Filtros | Soporte para interceptores que permiten modificar solicitudes o respuestas, ideal para tareas de registro (logging) o autenticación. |
Clientes de Servicio HTTP Declarativos (HTTP Service Clients)
La novedad más significativa en Spring Boot 4 es el soporte nativo y mejorado para los Clientes de Servicio HTTP Declarativos (Declarative HTTP Service Clients).
Este enfoque, conocido como el "Feign Killer", permite definir la comunicación con servicios externos mediante interfaces Java puras anotadas, eliminando la necesidad de escribir la implementación manual de RestClient.
- Simplificación de Configuración: Spring Framework 7 introduce la anotación
@ImportHttpServices, que, junto con la auto-configuración de Spring Boot 4, simplifica la creación de los proxies de cliente como beans de Spring, eliminando el tedioso código manual deHttpServiceProxyFactory.
Rendimiento y Alineación con Project Loom
Aunque RestClient es síncrono (bloqueante), está intrínsecamente ligado al aumento de rendimiento y escalabilidad que ofrece Project Loom (Hilos Virtuales - VTs) de Java 21+.
Concurrencia con Hilos Virtuales
Anteriormente, el código síncrono de RestTemplate limitaba la escalabilidad al consumir hilos de plataforma costosos mientras esperaba la I/O. RestClient se beneficia de la optimización del JVM: cuando un hilo virtual realiza una llamada de I/O (como una petición HTTP), se "estaciona" rápidamente (cede el hilo de plataforma subyacente), permitiendo que ese hilo de plataforma (carrier thread) ejecute otra tarea.
Eficiencia
Esto permite que las aplicaciones Spring MVC que utilizan RestClient manejen una concurrención extremadamente alta en cargas de trabajo limitadas por I/O (I/O-bound) utilizando código de bloqueo simple, alcanzando una escalabilidad comparable a la programación reactiva (WebClient) pero con menor complejidad de código. Los VTs son mucho más ligeros (alrededor de 1kB por hilo) que los hilos de plataforma tradicionales (que pueden usar de 1 a 8 MB).
Configuración de Rendimiento
La configuración del cliente para el uso de VTs en Spring Boot 4 se puede automatizar cuando spring.threads.virtual.enabled está en true. Los timeouts de conexión y lectura también se centralizan mediante propiedades de configuración unificada como spring.http.clients.connect-timeout y spring.http.clients.read-timeout.
Nueva Sintaxis: Comparación con RestTemplate y Ejemplos
Comparación de Sintaxis: RestClient vs. RestTemplate
El cambio más evidente es la adopción del API Fluido (Fluent API) en RestClient, que reemplaza el patrón de plantilla (Template Pattern) verboso y lleno de sobrecargas de RestTemplate.
| Característica | RestTemplate (Estilo Antiguo) | RestClient (Estilo Fluido Moderno) |
|---|---|---|
| Estado | Deprecado desde Spring Framework 6.1; programado para ser eliminado en Spring Framework 8.0. | Estándar de facto para síncrono. |
| Creación Básica | var restTemplate = new RestTemplate(); | var restClient = RestClient.create(); |
| Obtener (GET) | String res = restTemplate.getForObject(url, String.class); | String res = restClient.get().uri(url).retrieve().body(String.class); |
| Enviar (POST) | ResponseEntity<T> res = restTemplate.postForEntity(url, request, T.class); | ResponseEntity<T> res = restClient.post().uri(url).body(request).retrieve().toEntity(T.class); |
Varios Ejemplos Claros y Explicados (RestClient)
Creación de Bean Configurado
Es una práctica recomendada en Spring Boot inyectar el RestClient.Builder para crear un bean con configuraciones globales (URL base, encabezados, etc.).
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
@Configuration
public class RestClientConfiguration {
@Bean
public RestClient githubRestClient(RestClient.Builder builder) {
// Configura una URL base y un encabezado por defecto para todas las solicitudes
return builder
.baseUrl("https://api.github.com")
.defaultHeader("Accept", "application/json")
.build();
}
}Petición GET con Variables de Ruta y Query Parameters
Muestra cómo inyectar variables en el URI y añadir parámetros de consulta.
// Suponiendo que 'githubRestClient' ya está inyectado
public String getRepositoryInfo(String owner, String repo, boolean verbose) {
String uriTemplate = "/repos/{owner}/{repo}";
return githubRestClient.get()
.uri(uriTemplate, owner, repo) // Mapea {owner} y {repo}
.queryParam("verbose", verbose) // Añade ?verbose=true o false
.retrieve()
.body(String.class);
}Petición POST (Creación de un Recurso)
El cliente serializa automáticamente el objeto Java (por ejemplo, NewUserDTO) a JSON y maneja la respuesta.
import org.springframework.http.MediaType;
public User createNewUser(NewUserDTO userData) {
return githubRestClient.post()
.uri("/users")
.contentType(MediaType.APPLICATION_JSON) // Establece Content-Type
.body(userData) // Objeto que se serializará a JSON
.retrieve()
.body(User.class); // Deserializa la respuesta a un objeto User
}Manejo de Errores Basado en Estado HTTP
Permite definir un manejo de errores específico antes de que RestClient lance una excepción genérica RestClientException.
import org.springframework.http.HttpStatus;
public User safeGetUser(long id) {
return githubRestClient.get()
.uri("/users/{id}", id)
.retrieve()
.onStatus(HttpStatus.NOT_FOUND, (request, response) -> {
// Manejo específico para 404
System.out.println("Usuario no encontrado, devolviendo default");
// Se puede lanzar una excepción o, en este caso, devolver un valor predeterminado
throw new CustomResourceNotFoundException("User ID " + id + " not found");
})
.onStatus(HttpStatus::is5xxServerError, (request, response) -> {
// Manejo para 5xx (errores de servidor)
throw new RuntimeException("Error interno del servicio remoto.");
})
.body(User.class);
}Declarative HTTP Client (Spring Boot 4)
Este es el patrón más moderno, utilizando interfaces anotadas con @HttpExchange.
// 1. Interfaz de Cliente (Definición del Contrato)
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.HttpExchange;
@HttpExchange(url = "https://api.external.com/api/v1") // Base URL
public interface ExternalApi {
@GetExchange("/items/{itemId}") // Mapea a GET https://api.external.com/api/v1/items/{itemId}
Item getItem(@PathVariable String itemId);
}
// 2. Uso en un Servicio (Spring Boot 4.0 inyecta el proxy automáticamente tras configurar @ImportHttpServices)
@Service
public class ItemService {
private final ExternalApi externalApi;
// Se inyecta la implementación proxy generada
public ItemService(ExternalApi externalApi) {
this.externalApi = externalApi;
}
public Item findItem(String id) {
// Se siente como llamar a un método local
return externalApi.getItem(id);
}
}Recomendación: Dado que RestTemplate está en camino de ser eliminado (en Spring Framework 8.0), y el nuevo modelo síncrono de RestClient está optimizado para los Hilos Virtuales, se recomienda encarecidamente migrar todos los proyectos de Spring MVC a RestClient y adoptar la aproximación Declarativa (@HttpExchange) para todos los nuevos clientes service-to-service. Esto garantiza un código más limpio, fácil de mantener y preparado para la alta concurrencia moderna de Java.