Spring Boot 3.x en 2026: virtual threads, RestClient y el stack moderno Java

La versión 3 de Spring Boot, publicada en noviembre de 2022, trae dos requisitos que no tienen vuelta atrás: Java 17 como mínimo y la migración completa a Jakarta EE 9+. Eso significa que todos los imports que empezaban por javax.* pasan a jakarta.*.

El cambio más habitual es en persistencia y en el API de servlets:

// Antes (Spring Boot 2.x)
import javax.persistence.*;
import javax.servlet.*;

// Ahora (Spring Boot 3.x)
import jakarta.persistence.*;
import jakarta.servlet.*;

Si tienes un proyecto grande, hacer eso a mano es una pesadilla. Por suerte, Spring Migration Assistant usa recetas de OpenRewrite para automatizar la mayor parte del trabajo. No lo resuelve todo, pero se encarga del 80% del proceso.

Lo que no cambia: @SpringBootApplication, el modelo de autoconfiguración y la estructura general de las clases. Solo cambian los namespaces de las dependencias externas.

Virtual threads en Spring Boot 3.2: una sola línea basta

Con Spring Boot 3.2 (noviembre de 2023) llegó algo que llevaba tiempo esperándose: soporte oficial de virtual threads de Java 21. Activarlos es trivial, añades esto en application.properties:

spring.threads.virtual.enabled=true

Spring Boot se encarga del resto. Tomcat, Jetty y el executor de async empiezan a usar virtual threads sin que toques ni una línea de código de negocio. Las peticiones HTTP que antes bloqueaban un thread del pool ahora no bloquean nada.

¿Cuándo nota el cambio? En apps con muchas peticiones concurrentes que hacen I/O: consultas a BBDD, llamadas a APIs externas, lectura de archivos. Si tu aplicación es CPU-bound, los virtual threads no aportan diferencia apreciable.

RestClient: fuera RestTemplate

Spring 6.1 (incluido en Boot 3.2) introduce RestClient, que reemplaza a RestTemplate. La API es fluida, síncrona y mucho más cómoda de leer:

RestClient client = RestClient.create("https://api.ejemplo.com");

UserDto user = client.get()
    .uri("/users/{id}", 1)
    .retrieve()
    .body(UserDto.class);

RestClient es la opción para código síncrono, especialmente combinado con virtual threads. Si usas Reactor o WebFlux, WebClient sigue siendo lo tuyo. RestTemplate no ha desaparecido aún, pero está en camino de quedar deprecado a largo plazo, así que no lo uses en código nuevo.

Actuator y observabilidad en Spring Boot 3

Spring Boot Actuator expone endpoints que te permiten monitorizar la aplicación sin instrumentación manual. Los más usados son /actuator/health, /actuator/metrics e /actuator/info.

Para métricas, Micrometer es la pieza central. Funciona con Prometheus, Datadog, New Relic y otros sistemas. Añades la dependencia del backend que uses y listo, las métricas fluyen solas.

Para trazas distribuidas está Micrometer Tracing, el sucesor de Spring Sleuth. Compatible con Zipkin y con OpenTelemetry, que es el estándar que más tracción tiene ahora mismo en entornos Kubernetes.

Spring Boot 3.4 (noviembre de 2024) añade structured logging: los logs salen en JSON con campos definidos, lo que facilita mucho la ingesta en ELK Stack o Grafana Loki sin parsers personalizados.

Spring Data: lo esencial para persistencia

Spring Data JPA sigue siendo la forma más habitual de acceder a bases de datos relacionales. Con @Repository y la convención de nombres en los métodos, la mayor parte de las queries se escriben solas:

List<Producto> findByNombreContaining(String texto);
List<Pedido> findByClienteIdAndEstado(Long clienteId, Estado estado);

Para queries más complejas tienes @Query con JPQL o SQL nativo, y Specifications para queries dinámicas en las que los filtros varían según lo que llegue del usuario.

Un problema que aparece casi siempre en JPA es el N+1: cargar una lista de entidades y que cada una lance una query adicional para cargar sus relaciones. @EntityGraph lo resuelve en el propio repositorio:

@EntityGraph(attributePaths = {"pedidos"})
List<Cliente> findAll();

Spring Data 3.x también mejora el soporte de records de Java 21 y la integración con Kotlin, aunque eso ya depende de tu stack concreto.

Seguridad con Spring Security 6

Si venías de Spring Security 5, el cambio más visible es que WebSecurityConfigurerAdapter desaparece. Ahora la configuración se hace con un bean de tipo SecurityFilterChain:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(auth -> auth
        .requestMatchers("/api/**").authenticated()
        .anyRequest().permitAll()
    );
    return http.build();
}

Para APIs que usan JWT con OAuth2 Resource Server, la configuración es igualmente directa:

http.oauth2ResourceServer(oauth2 ->
    oauth2.jwt(Customizer.withDefaults())
);

Spring Security valida y parsea los tokens sin que necesites librerías adicionales. Solo tienes que configurar el jwk-set-uri de tu proveedor de identidad en application.properties y el framework se encarga del resto.

Si quieres profundizar en cómo proteger tus endpoints, en este artículo tienes el proceso completo: autenticación en Spring Boot: JWT, OAuth2 y MFA.

Testing en Spring Boot

Spring Boot ofrece varias anotaciones para testear cada capa de forma independiente, sin levantar todo el contexto:

  • @SpringBootTest: carga el contexto completo. Úsalo para tests de integración.
  • @WebMvcTest(UserController.class): solo el layer web, sin BBDD ni servicios reales.
  • @DataJpaTest: solo el layer de persistencia, con H2 en memoria.

Con @WebMvcTest y MockMvc puedes verificar las respuestas HTTP sin levantar un servidor:

mockMvc.perform(get("/api/users/1"))
    .andExpect(status().isOk())
    .andExpect(jsonPath("$.nombre").value("Ana"));

Spring Boot 3.3 añade @ServiceConnection, que arranca containers Docker automáticamente para los tests. Conectas una BBDD real en el test sin configurar nada a mano, Testcontainers se encarga de levantar el contenedor y Spring de conectarse a él.

Si quieres ver cómo encajan los tests con la capa de controladores, aquí lo tienes explicado con detalle: controladores en Spring Boot: la capa HTTP entre petición y respuesta.

AOT y GraalVM Native Image

Spring Boot tiene soporte de procesamiento AOT (Ahead-of-Time) para compilar aplicaciones con GraalVM Native Image. El resultado es un binario nativo que arranca en milisegundos y consume mucha menos RAM que la JVM tradicional.

Para generar una imagen Docker nativa con Maven:

./mvnw -Pnative spring-boot:build-image

El coste está en la compilación, que se vuelve mucho más lenta, y en algunas limitaciones con reflection. Tampoco es compatible con toda la magia de Spring basada en proxies dinámicos, así que tendrás que revisar si alguna librería de terceros da problemas.

¿Cuándo compensa? En microservicios serverless o en Kubernetes con escala a cero, donde arrancar rápido y usar poca memoria marcan la diferencia. En una aplicación monolítica que arranca una vez y corre semanas, la JVM sigue ganando en rendimiento sostenido gracias al JIT.

Imagen: Pexels / panumas nikhomkhai

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP