Skip to main content

Command Palette

Search for a command to run...

Spring Framework 7 y Spring Boot 4: Las Nuevas Características que Debes Conocer

Updated
8 min read

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

  1. Actualizar Java - Asegúrate de tener Java 21

  2. Actualizar dependencias - Cambia a Spring Boot 4.0.0

  3. Revisar anotaciones de nullabilidad - Migra a JSpecify si usas otras

  4. 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:

🔗 spring-framework7-samples

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ísticaBeneficio Principal
Versionado de APISin bibliotecas externas, configuración simple
JSpecifyEstándar unificado, mejor seguridad de tipos
RestTestClientPruebas más simples, sin dependencias reactivas
@HttpExchangeAlternativa ligera a Feign, integrada con Spring
ResilienciaSin dependencias adicionales, fácil de usar
TaskDecoratorComposició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

More from this blog

JoeDayz

52 posts

Community Guy | Java Champion | AWS Architect | Software Architect