Hasta ahora hemos construido la arquitectura por capas (Controller, Service, DAO/Repository) instanciando y conectando las dependencias de forma manual. En este tema utilizaremos Spring Boot, el framework estándar de la industria Java, para delegar esta tarea a su contenedor de IoC y exponer nuestra aplicación como una API REST profesional.

Un framework proporciona la infraestructura necesaria (enrutamiento HTTP, servidor web embebido, gestión de beans, filtros) para que el desarrollador se concentre únicamente en implementar la lógica de negocio.

Crear una aplicación web tradicional en Java requería configurar archivos de despliegue complejos, compilar un archivo .war y desplegarlo manualmente en un servidor externo (como Apache Tomcat). Spring Boot simplifica este proceso integrando un servidor Tomcat embebido dentro del propio ejecutable .jar de la aplicación.

Creación del proyecto

La forma estándar de inicializar un proyecto Spring Boot es a través de Spring Initializr. Al configurar un nuevo proyecto seleccionamos:

  • Gestor de dependencias: Maven.

  • Lenguaje: Java.

  • Versión de Spring Boot: 4.x.

  • Metadatos del proyecto: Group ID y Artifact ID.

  • Empaquetado: Jar.

  • Configuration: Properties

  • Versión de Java: 25.

Dependencias principales:

  • Spring Web: Incluye el servidor Tomcat embebido y todo el soporte para APIs REST.

Punto de entrada y configuración

La clase principal anotada con @SpringBootApplication es la encargada de arrancar el servidor embebido y la aplicación mediante el método main:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BookstoreApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(BookstoreApiApplication.class, args);
    }
}

Por defecto, el servidor web escucha en el puerto 8080. Para cambiar este comportamiento, basta con incluir la siguiente propiedad en el archivo src/main/resources/application.properties:

server.port=8081

Para comprender cómo procesa Spring Boot las peticiones HTTP, es necesario entender la especificación base del desarrollo web en Java: los Servlets.

Un Servlet (definido en la especificación jakarta.servlet) es una clase Java encargada de atender peticiones HTTP (HttpServletRequest) y generar respuestas HTTP (HttpServletResponse).

El patrón Front Controller (DispatcherServlet)

En lugar de requerir la creación de un Servlet independiente para cada ruta del sistema, Spring Boot implementa el patrón de diseño Front Controller a través de una clase central llamada DispatcherServlet.

ClienteServidor Web (Tomcat)Spring Context (IoC Container)Navegador / Cliente HTTPCadena de Filtros (Filter Chain)DispatcherServletHandler MappingHttp Message ConverterBookControllerBookService1. Petición HTTP2. Intercepta petición3. Asocia URI con Método4. Transforma JSON <-> Java DTO5. Ejecuta controlador6. Invoca capa de negocio

El flujo de ejecución de una petición sigue estos pasos:

  1. Petición del cliente: El cliente envía una petición HTTP (por ejemplo, GET /api/v1/books).

  2. Recepción en el Front Controller: El DispatcherServlet intercepta la petición entrante.

  3. Enrutamiento (Handler Mapping): Consulta sus tablas internas para localizar qué @RestController y qué método están configurados para responder a esa ruta y verbo HTTP.

  4. Deserialización: Transforma automáticamente el cuerpo JSON de la petición en un objeto Java (Record / DTO).

  5. Invocación: Ejecuta el método del controlador pasándole los argumentos procesados.

  6. Serialización y Respuesta: Toma el resultado devuelto por el controlador (un DTO o colección de DTOs), lo convierte al formato JSON y lo envía al cliente dentro de la respuesta HTTP.

Antes de que una petición alcance el DispatcherServlet (y, por extensión, nuestros controladores), es posible interceptarla mediante Filtros HTTP.

Los filtros forman una cadena de responsabilidad (Filter Chain) y se utilizan para procesar aspectos transversales a la aplicación, tales como:

  • Registro de logs de auditoría (tiempo de respuesta, dirección IP de origen).

  • Verificación de cabeceras de autenticación o seguridad.

  • Gestión de cabeceras CORS (Cross-Origin Resource Sharing).

Ejemplo: Creación de un filtro personalizado

Extendiendo de la clase OncePerRequestFilter, garantizamos que el filtro se ejecute exactamente una vez por cada petición HTTP:


import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class AuditLogFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {
    
        long startTime = System.currentTimeMillis();
    
        // Permite que la petición continúe hacia el DispatcherServlet / Controller
        filterChain.doFilter(request, response);
    
        long duration = System.currentTimeMillis() - startTime;
        IO.printf("[%s] %s -> Estado: %d (%d ms)%n", 
                request.getMethod(), request.getRequestURI(), response.getStatus(), duration);
    }
}

Para exponer recursos como endpoints REST, creamos clases anotadas con @RestController y @RequestMapping.

Anotaciones de mapeo HTTP

  • @RestController: Define la clase como un controlador web REST e indica a Spring que el valor devuelto por sus métodos debe serializarse directamente en el cuerpo de la respuesta HTTP (habitualmente en formato JSON).

  • @RequestMapping(“/api/v1/books”): Define la ruta base para todos los endpoints contenidos en esa clase.

  • @GetMapping: Asocia el método a peticiones GET (lectura).

  • @PostMapping: Asocia el método a peticiones POST (creación).

  • @PutMapping: Asocia el método a peticiones PUT (actualización completa).

  • @DeleteMapping: Asocia el método a peticiones DELETE (eliminación).

Extraer datos de la Petición

  • @PathVariable: Extrae valores incrustados en la propia URL (ejemplo: /api/v1/books/{id}).

  • @RequestParam: Extrae parámetros de consulta pasados tras el símbolo ? (ejemplo: /api/v1/books?page=2).

  • @RequestBody: Deserializa automáticamente el cuerpo JSON de la petición entrante en un objeto Java (Record / DTO).

En el diseño de APIs REST, el controlador debe devolver tanto los datos formateados (DTOs) como el código de estado HTTP correspondiente. Para lograr un control explícito sobre la respuesta se utiliza la clase ResponseEntity<T>.

Ejemplo práctico: BookController


import com.fpmislata.bookstoreapi.controller.dto.BookCreateRequest;
import com.fpmislata.bookstoreapi.controller.dto.BookResponse;
import com.fpmislata.bookstoreapi.service.BookService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/v1/books")
public class BookController {

private final BookService bookService;

    // Inyección de dependencias por constructor realizada automáticamente por Spring
    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    // GET /api/v1/books -> 200 OK con el listado
    @GetMapping
    public ResponseEntity<List<BookResponse>> getAll() {
        List<BookResponse> books = bookService.findAll();
        return ResponseEntity.ok(books); // Estado 200 OK
    }

    // GET /api/v1/books/12 -> 200 OK si existe
    @GetMapping("/{id}")
    public ResponseEntity<BookResponse> getById(@PathVariable Long id) {
        BookResponse book = bookService.findById(id);
        return ResponseEntity.ok(book); // Estado 200 OK
    }

    // POST /api/v1/books -> 201 Created con el objeto creado
    @PostMapping
    public ResponseEntity<BookResponse> create(@RequestBody BookCreateRequest request) {
        BookResponse createdBook = bookService.create(request);
        return ResponseEntity.status(HttpStatus.CREATED).body(createdBook); // Estado 201 Created
    }

    // DELETE /api/v1/books/12 -> 204 No Content
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        bookService.delete(id);
        return ResponseEntity.noContent().build(); // Estado 204 No Content
    }
}

Spring detecta y gestiona de forma automática el ciclo de vida de nuestras clases mediante el escaneo de anotaciones:

Anotación Capa de la Arquitectura Descripción
@RestController Presentación Expone endpoints REST y serializa las respuestas en JSON.
@Service Negocio Contiene la lógica de negocio del sistema.
@Repository Persistencia Implementa el acceso a datos (DAOs / Repositorios).
@Component Genérica Para componentes o utilidades transversales (Filtros, Mapeadores).

Al aplicar @Service en la capa de negocio y @Repository en la capa de persistencia, el contenedor de IoC de Spring resuelve e inyecta las dependencias automáticamente a través del constructor del @RestController, eliminando por completo la necesidad de instanciarlas manualmente.

  • clase/daw/prog/2eval/spring.txt
  • Última modificación: 2026/08/25 12:22
  • por cesguiro