Spring Framework 7 y Spring Boot 4: Las Nuevas Características que Debes Conocer
El ecosistema Spring continúa evolucionando, y con Spring Framework 7 y Spring Boot 4 tenemos nuevas herramientas que hacen el desarrollo más simple, seguro y productivo. En este artículo, exploraremos las características más destacadas que debes conocer.
Introducción
Spring Framework 7 y Spring Boot 4 han llegado con una serie de mejoras significativas que simplifican el desarrollo de aplicaciones Java modernas. En este artículo, exploraremos las características más destacadas que hacen que estas versiones sean un paso adelante en el ecosistema Spring.
¿Qué hay de Nuevo?
Spring Framework 7 y Spring Boot 4 traen consigo mejoras importantes:
✅ Versionado de API nativo - Sin necesidad de bibliotecas externas
✅ Null Safety con JSpecify - Mejor seguridad de tipos
✅ Clientes HTTP declarativos - Alternativa ligera a Feign
✅ Resiliencia integrada - Reintentos y límites de concurrencia sin dependencias adicionales
✅ RestTestClient - Cliente de pruebas más simple
✅ Múltiples TaskDecorator - Composición automática de decoradores
1. Versionado de API REST Nativo
Una de las características más esperadas es el soporte nativo para el versionado de APIs. Ya no necesitas bibliotecas externas o configuraciones complejas.
Antes (Spring Framework 6)
// Tenías que usar bibliotecas externas o crear tu propia solución
@GetMapping("/api/v1/accounts/{id}")
public Account getAccountV1(@PathVariable Long id) { ... }
@GetMapping("/api/v2/accounts/{id}")
public AccountV2 getAccountV2(@PathVariable Long id) { ... }
Ahora (Spring Framework 7)
@RestController
@RequestMapping("/accounts")
public class AccountController {
@GetMapping(path = "/{id}", version = "1.0")
public ResponseEntity<Account> getAccountV1_0(@PathVariable Long id) {
// Versión 1.0 - Información básica
}
@GetMapping(path = "/{id}", version = "1.1")
public ResponseEntity<Account> getAccountV1_1(@PathVariable Long id) {
// Versión 1.1 - Información completa
}
@GetMapping(path = "/{id}", version = "2.0")
public ResponseEntity<AccountResponseV2> getAccountV2_0(@PathVariable Long id) {
// Versión 2.0 - Formato mejorado
}
}
Configuración
@Configuration
public class ApiVersioningConfig implements WebMvcConfigurer {
@Override
public void configureApiVersioning(@NonNull ApiVersionConfigurer configurer) {
configurer.useRequestHeader("X-API-Version");
}
}
Uso
curl -H "X-API-Version: 1.0" http://localhost:8080/accounts/1
curl -H "X-API-Version: 2.0" http://localhost:8080/accounts/1
Estrategias disponibles:
Request Header (
X-API-Version)Query Parameter (
?version=1.0)Path Segment (
/api/v1/accounts)Media Type Parameter (
Accept: application/json;version=1.0)
2. Null Safety con JSpecify
Spring Framework 7 adopta JSpecify como estándar para anotaciones de nullabilidad, unificando las múltiples anotaciones que existían en el ecosistema Java.
El Problema Anterior
Antes teníamos múltiples estándares:
@Nonnull/@Nullable(JSR-305)@NotNull/@Nullable(JetBrains)@NonNull/@Nullable(Lombok)
La Solución: JSpecify
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
@Service
public class UserService {
// @NonNull garantiza que nunca retorna null
public @NonNull Map<Long, User> getAllUsers() {
return new HashMap<>(users);
}
// @Nullable indica que puede retornar null
public @Nullable User getUserById(@NonNull Long id) {
return users.get(id);
}
// Parámetros @NonNull son requeridos, @Nullable son opcionales
public @NonNull User createUser(
@NonNull String name, // Requerido
@NonNull String email, // Requerido
@Nullable String phone) { // Opcional
return new User(name, email, phone);
}
}
Beneficios
✅ Estándar unificado - Una sola forma de hacer las cosas
✅ Mejor IDE - Detecta posibles
NullPointerException✅ Kotlin interop - Mejora la interoperabilidad
✅ Seguridad de tipos - Ayuda a prevenir errores en tiempo de compilación
3. RestTestClient: Pruebas Más Simples
Spring Framework 7 introduce RestTestClient, un cliente ligero para probar endpoints REST sin necesidad de dependencias reactivas.
Antes: MockMvc o WebTestClient
// MockMvc - Verboso
mockMvc.perform(get("/accounts/1")
.header("X-API-Version", "1.0"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1));
// WebTestClient - Requiere WebFlux
webTestClient.get()
.uri("/accounts/1")
.header("X-API-Version", "1.0")
.exchange()
.expectStatus().isOk();
Ahora: RestTestClient
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class AccountControllerTest {
RestTestClient client;
@BeforeEach
void setUp(WebApplicationContext context) {
client = RestTestClient.bindToApplicationContext(context).build();
}
@Test
void testGetAccount() {
client.get()
.uri("/accounts/1")
.header("X-API-Version", "1.0")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.id").exists();
}
}
Ventajas:
✅ Sin dependencias reactivas
✅ API fluida y fácil de usar
✅ Más simple que MockMvc para casos básicos
4. Clientes HTTP Declarativos con @HttpExchange
¿Te acuerdas de Feign? Spring Framework 7 trae una alternativa nativa y más ligera: @HttpExchange.
Definir el Cliente
@HttpExchange
public interface QuoteClient {
@GetExchange("/jokes/random")
ChuckNorrisJoke getRandomJoke();
@GetExchange("/jokes/random")
ChuckNorrisJoke getRandomJokeByCategory(@RequestParam("category") String category);
@GetExchange("/jokes/categories")
String[] getCategories();
}
Configurar el Cliente
@Configuration
public class HttpClientConfig {
@Bean
public QuoteClient quoteClient() {
RestClient restClient = RestClient.builder()
.baseUrl("https://api.chucknorris.io")
.build();
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder()
.exchangeAdapter(RestClientAdapter.create(restClient))
.build();
return factory.createClient(QuoteClient.class);
}
}
Usar el Cliente
@RestController
@RequestMapping("/quotes")
public class QuoteController {
private final QuoteClient quoteClient;
public QuoteController(QuoteClient quoteClient) {
this.quoteClient = quoteClient;
}
@GetMapping("/random")
public String getRandomQuote() {
return quoteClient.getRandomJoke().getValue();
}
}
Ventajas sobre Feign:
✅ Sin dependencias externas
✅ Integrado con Spring Framework
✅ Más ligero
✅ Type-safe
5. Anotaciones de Resiliencia Integradas
Spring Framework 7 introduce anotaciones de resiliencia (@Retryable y @ConcurrencyLimit) que eliminan la necesidad de bibliotecas como Resilience4j.
Habilitar Resiliencia
@Configuration
@EnableResilientMethods
public class HttpClientConfig {
// Las anotaciones @Retryable y @ConcurrencyLimit ahora están activas
}
Usar las Anotaciones
@HttpExchange
public interface QuoteClient {
@GetExchange("/jokes/random")
@Retryable // Reintenta automáticamente si falla
@ConcurrencyLimit(3) // Limita a 3 llamadas concurrentes
ChuckNorrisJoke getRandomJoke();
}
@Service
public class ResilientQuoteService {
@Retryable
public ChuckNorrisJoke getRandomJokeWithRetry() {
return quoteClient.getRandomJoke();
}
@ConcurrencyLimit(5)
public String[] getCategoriesWithLimit() {
return quoteClient.getCategories();
}
@Retryable
@ConcurrencyLimit(3)
public ChuckNorrisJoke getRandomJokeResilient() {
return quoteClient.getRandomJoke();
}
}
Beneficios:
✅ Sin bibliotecas adicionales
✅ Integrado con Spring
✅ Fácil de usar
✅ Verificable en runtime
6. Múltiples TaskDecorator Beans
Una mejora sutil pero poderosa: ahora puedes definir múltiples TaskDecorator beans y Spring los compone automáticamente.
Antes: Decorador Compuesto Manual
@Bean
TaskDecorator compositeDecorator() {
TaskDecorator logging = ...;
TaskDecorator measuring = ...;
// Tenías que crear un decorador compuesto manualmente
return runnable -> logging.decorate(measuring.decorate(runnable));
}
Ahora: Composición Automática
@Configuration
public class TaskDecoratorConfiguration {
@Bean
@Order(2) // Se aplica después (más interno)
public TaskDecorator loggingTaskDecorator() {
return runnable -> () -> {
log.info("Running Task: {}", runnable);
try {
runnable.run();
} finally {
log.info("Finished Task: {}", runnable);
}
};
}
@Bean
@Order(1) // Se aplica primero (más externo)
public TaskDecorator measuringTaskDecorator() {
return runnable -> () -> {
final var startTime = System.currentTimeMillis();
try {
runnable.run();
} finally {
final var endTime = System.currentTimeMillis();
log.info("Finished within {}ms", endTime - startTime);
}
};
}
}
Usar con @Async
@Component
public class HelloWorldEventLogger {
@Async
@EventListener
public void logHelloWorldEvent(HelloWorldEvent event) {
log.info("Hello World Event: {}", event.message());
}
}
Los decoradores se aplican automáticamente en el orden especificado por @Order.
Beneficios:
✅ Sin código boilerplate
✅ Orden configurable con
@Order✅ Composición automática
✅ Separación de concerns
Requisitos y Migración
Requisitos
Java 21 o superior (requisito obligatorio)
Spring Boot 4.0.0
Spring Framework 7.0.1
Migración desde Spring Boot 3
Actualizar Java - Asegúrate de tener Java 21
Actualizar dependencias - Cambia a Spring Boot 4.0.0
Revisar anotaciones de nullabilidad - Migra a JSpecify si usas otras
Actualizar tests - Considera usar
RestTestClient
Ejemplo Completo: Proyecto de Demostración
He creado un proyecto completo que demuestra todas estas características. Puedes encontrarlo en GitHub:
El proyecto incluye:
✅ Ejemplos funcionales de todas las características
✅ Tests completos (34 tests, todos pasando)
✅ Documentación paso a paso
✅ README detallado para seguir en un webinar
✅ Código listo para ejecutar
Clonar y Ejecutar
git clone https://github.com/josediaz/spring-framework7-samples.git
cd spring-framework7-samples
mvn clean test
Estructura del Proyecto
spring-framework7-samples/
├── src/main/java/
│ ├── controller/ # Controladores con versionado
│ ├── client/ # Clientes HTTP declarativos
│ ├── config/ # Configuraciones
│ ├── service/ # Servicios con resiliencia
│ └── model/ # Modelos con JSpecify
└── src/test/java/ # Tests con RestTestClient
Resumen
Spring Framework 7 y Spring Boot 4 representan un paso importante hacia un desarrollo Java más moderno y seguro:
| Característica | Beneficio Principal |
| Versionado de API | Sin bibliotecas externas, configuración simple |
| JSpecify | Estándar unificado, mejor seguridad de tipos |
| RestTestClient | Pruebas más simples, sin dependencias reactivas |
| @HttpExchange | Alternativa ligera a Feign, integrada con Spring |
| Resiliencia | Sin dependencias adicionales, fácil de usar |
| TaskDecorator | Composición automática, menos boilerplate |
¿Vale la Pena Migrar?
Sí, definitivamente. Las mejoras en seguridad de tipos, simplicidad de configuración y nuevas capacidades hacen que la migración valga la pena, especialmente si:
Estás usando Java 21 o planeas migrar
Necesitas versionado de API
Quieres mejorar la seguridad de tipos con JSpecify
Buscas simplificar tus tests y clientes HTTP
Conclusión
Spring Framework 7 y Spring Boot 4 no son solo actualizaciones incrementales; traen mejoras significativas que simplifican el desarrollo y mejoran la calidad del código. Desde el versionado de API nativo hasta las anotaciones de resiliencia integradas, estas versiones hacen que Spring sea aún más poderoso y fácil de usar.
¿Qué te parece Spring Framework 7? ¿Ya lo has probado en tus proyectos?
Déjame saber en los comentarios qué característica te parece más interesante o si tienes alguna pregunta sobre la migración.
Recursos Adicionales
¿Te gustó este artículo? Compártelo en tus redes sociales y sígueme en Twitter para más contenido sobre Spring Framework y desarrollo Java.
Sobre el Autor
José Díaz es desarrollador Java y Spring con más de X años de experiencia. Especializado en arquitectura de software y mejores prácticas de desarrollo. Puedes encontrarlo en blog.joedayz.pe y en sus redes sociales.
Este artículo fue publicado originalmente en blog.joedayz.pe




