Java Framework

Spring Boot

Spring Boot es la capa basada en opiniones sobre Spring Framework que convierte un montón de configuración en una aplicación en ejecución: starters, auto-configuración y un servidor embebido.

advanced16 min readUpdated 16 sept 2026
PostController.java
java
// src/main/java/com/example/blog/PostController.java
@RestController
@RequestMapping("/api/posts")
public class PostController {

    private final PostService postService;

    public PostController(PostService postService) {
        this.postService = postService;
    }

    @GetMapping
    public List<PostResponse> list() {
        return postService.findAll();
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public PostResponse create(@Valid @RequestBody CreatePostRequest request) {
        return postService.create(request);
    }
}
Lanzado
2014
Basado en
Spring Framework
Lenguaje
Java, Kotlin, Groovy
Servidor
Tomcat, Jetty o Netty embebidos
Estilo
Convención y auto-configuración
Versión
3.x

Por que importa

Por qué Spring Boot domina la JVM

En ejecución en minutos

Un starter y un método main te brindan una aplicación con estructura de producción y un servidor embebido, por lo que no hay nada que desplegar antes de escribir código.

Auto-configuración

Spring Boot inspecciona el classpath y configura los beans que encuentra. Añade un controlador de base de datos y aparecerá un DataSource listo para usar.

Un ecosistema coherente

Spring Data, Security, Batch, Integration y Cloud comparten el mismo modelo de programación, por lo que las habilidades se transfieren a toda la plataforma.

La imagen completa

Auto-config, DI, starters

Los starters agrupan dependencias coherentes, la auto-configuración las conecta mediante valores predeterminados sensatos y el contenedor inyecta los objetos que tus clases necesitan.

Auto-configuración

Bootstrap

La configuración condicional lee lo que hay en el classpath y proporciona valores predeterminados, que puedes sobrescribir con propiedades en lugar de código.

Capa web anotada

Exponer

Las anotaciones mapean métodos y rutas HTTP a métodos de Java, y los conversores de mensajes transforman objetos en JSON automáticamente.

Spring Data JPA

Persistir

Las interfaces de repositorio te ofrecen CRUD, métodos de consulta y paginación sin necesidad de implementaciones escritas a mano.

HTML5 de un vistazo

La caja de herramientas de Spring Boot

Servidor embebido

Tomcat, Jetty o Netty se ejecutan dentro del proceso como una librería de Java común.

Starters

spring-boot-starter-web integra todo el stack web en una sola dependencia.

Anotaciones

@RestController, @GetMapping y @RequestBody describen la API.

Inyección de dependencias

El contexto de la aplicación crea y conecta beans mediante inyección por constructor.

Spring Data JPA

Las interfaces de repositorio reemplazan el código repetitivo de acceso a datos.

Actuator

Los endpoints de salud, métricas e información están a una sola dependencia de distancia.

Una breve historia

De Spring XML a un main de una sola línea

  1. 2012

    Se anuncia Spring Boot

    El equipo de Spring presenta una forma de iniciar aplicaciones sin el XML que había crecido alrededor del framework.

    12
  2. 2014

    Spring Boot 1.0

    El primer lanzamiento GA convierte a los starters, la auto-configuración y el servidor embebido en la forma estándar de construir apps de Spring.

    14
  3. 2018

    Spring Boot 2.0

    Spring 5 trae WebFlux, un Actuator reescrito y soporte de primera clase para Kotlin.

    18
  4. 2022

    Spring Boot 3.0

    Java 17 se convierte en la base, el namespace jakarta reemplaza a javax y las imágenes nativas de GraalVM maduran.

    22
  5. 2024

    Spring Boot 3.4

    El logging estructurado, la observabilidad mejorada y las bases de dependencias actualizadas mantienen la plataforma al día.

    24
  6. Hoy

    El stack JVM por defecto

    Spring Boot es el punto de partida asumido para nuevos servicios de Java, desde monolitos hasta microservicios cloud-native.

    Hoy

La guia completa

Spring Boot: Todo lo que necesitas saber

¿Qué es Spring Boot?

Spring Boot es una capa sobre el Spring Framework que elimina la configuración que Spring requería anteriormente. En lugar de archivos XML, configuración de servlets y un contenedor donde desplegar, escribes una clase con un método main, añades una dependencia starter y obtienes una aplicación en funcionamiento con un servidor embebido.

Tres conceptos hacen el trabajo pesado. Los Starters agrupan las dependencias de una funcionalidad en una sola línea. La Auto-configuration inspecciona el classpath y configura beans basándose en valores predeterminados coherentes. El servidor embebido significa que la aplicación es un proceso de Java normal; no hay nada que instalar ni donde desplegar.

El resultado es que Spring Boot se ha convertido en la forma predeterminada de construir servicios en la JVM, desde pequeñas API internas hasta grandes flotas de microservicios, manteniendo todo el poder del ecosistema de Spring en su base.

El ecosistema de Spring en un minuto

Spring no es una sola librería, sino una familia que comparte un modelo de programación:

  • Spring Framework — el contenedor núcleo, la inyección de dependencias y la capa web.
  • Spring Data — repositorios sobre almacenes SQL y NoSQL.
  • Spring Security — autenticación y autorización.
  • Spring Batch / Integration — procesamiento por lotes y mensajería.
  • Spring Cloud — configuración, descubrimiento y resiliencia para sistemas distribuidos.

Debido a que están diseñados en conjunto, un proyecto puede adoptarlos uno a uno sin cambiar la forma en que está escrito. Esa consistencia es la verdadera razón por la cual los equipos estandarizan su desarrollo en Spring.

Starters y gestión de dependencias

Un starter es un conjunto seleccionado de dependencias que funcionan entre sí. Declaras la funcionalidad, no los jars individuales.

<!-- pom.xml -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

El equivalente en Gradle es igual de corto:

dependencies {
  implementation "org.springframework.boot:spring-boot-starter-web"
  implementation "org.springframework.boot:spring-boot-starter-data-jpa"
  implementation "org.springframework.boot:spring-boot-starter-validation"
  runtimeOnly "org.postgresql:postgresql"
}

El POM padre o el plugin de dependency-management fija las versiones compatibles por ti. Sobrescribe una versión solo cuando tengas un motivo justificado y deja que la plataforma gestione el resto.

La clase de aplicación y el servidor embebido

Toda aplicación de Spring Boot comienza aquí. Esta única clase es suficiente para lanzar un servidor web.

package com.example.blog;

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

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

@SpringBootApplication combina tres anotaciones: @Configuration, @EnableAutoConfiguration y @ComponentScan. El escaneo de componentes comienza en el paquete de esta clase y busca hacia abajo, razón por la cual la clase principal debe estar en la raíz de la estructura de tus paquetes.

Ejecutar ./mvnw spring-boot:run inicia un Tomcat embebido en el puerto 8080. Puedes cambiarlo por Jetty o Netty modificando una dependencia, y el código de la aplicación no se ve afectado.

La capa web anotada

Spring MVC mapea las solicitudes HTTP a métodos mediante anotaciones. @RestController combina @Controller y @ResponseBody, de modo que los objetos devueltos se serializan a JSON mediante Jackson.

@RestController
@RequestMapping("/api/posts")
public class PostController {

    private final PostService service;

    public PostController(PostService service) {
        this.service = service;
    }

    @GetMapping
    public List<PostResponse> list(
            @RequestParam(defaultValue = "0") int page) {
        return service.findPage(page);
    }

    @GetMapping("/{id}")
    public PostResponse get(@PathVariable Long id) {
        return service.findById(id);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public PostResponse create(@Valid @RequestBody CreatePostRequest request) {
        return service.create(request);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable Long id) {
        service.delete(id);
    }
}

@PathVariable lee desde la URL, @RequestParam desde la query string y @RequestBody deserializa el payload. Mantén los controladores ligeros: su función es traducir HTTP en una llamada a un servicio y traducir el resultado de vuelta. La lógica de dominio pertenece a la capa de servicio.

Inyección de dependencias y el contexto de la aplicación

El contexto de la aplicación es el contenedor que crea los objetos, llamados beans, y suministra sus dependencias. Tú declaras lo que una clase necesita y Spring decide cómo construirlo.

@Service
public class PostService {

    private final PostRepository repository;
    private final Clock clock;

    public PostService(PostRepository repository, Clock clock) {
        this.repository = repository;
        this.clock = clock;
    }

    @Transactional
    public PostResponse create(CreatePostRequest request) {
        var post = new Post(request.title(), request.body(), clock.instant());
        return PostResponse.from(repository.save(post));
    }
}

Las anotaciones de estereotipo marcan las clases que deben gestionarse: @Component para beans genéricos, @Service para servicios de dominio, @Repository para acceso a datos y @RestController para endpoints web. Para cualquier cosa que no puedas anotar —como una clase de terceros o un cliente configurado— decláralo en una clase @Configuration.

@Configuration
class ClockConfig {

    @Bean
    Clock clock() {
        return Clock.systemUTC();
    }
}

Prioriza la inyección por constructor en todo momento. Esto hace que las dependencias sean explícitas, permite usar campos final y te permite instanciar la clase en una prueba unitaria sencilla sin necesidad de iniciar Spring.

Spring Data JPA y la base de datos

Las entidades mapean objetos Java a tablas, y los repositorios te brindan acceso a los datos sin necesidad de una implementación.

@Entity
@Table(name = "posts")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 120)
    private String title;

    @Column(nullable = false)
    private String body;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Author author;

    protected Post() {
    }

    public Post(String title, String body, Author author) {
        this.title = title;
        this.body = body;
        this.author = author;
    }

    // getters and domain methods
}
public interface PostRepository extends JpaRepository<Post, Long> {

    List<Post> findByPublishedTrueOrderByCreatedAtDesc();

    @Query("select p from Post p join fetch p.author where p.id = :id")
    Optional<Post> findWithAuthor(Long id);
}

Spring Data deriva una consulta a partir del nombre del método, por lo que findByPublishedTrueOrderByCreatedAtDesc no necesita cuerpo. Usa @Query cuando el nombre derivado sea ilegible, y join fetch para evitar el problema de N+1 del lazy-loading. Mantén ddl-auto en validate en producción y gestiona el esquema con Flyway o Liquibase.

Configuración con application.yml y perfiles

La configuración reside en src/main/resources y puede ser sobrescrita mediante variables de entorno, lo que permite que el mismo jar se ejecute en cualquier lugar.

# src/main/resources/application.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/blog
    username: blog
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
    open-in-view: false
server:
  port: 8080

Los perfiles permiten que una misma compilación se comporte de manera diferente según el entorno.

# src/main/resources/application-prod.yml
spring:
  datasource:
    url: ${DATABASE_URL}
logging:
  level:
    root: warn

Activa un perfil con --spring.profiles.active=prod o mediante la variable de entorno SPRING_PROFILES_ACTIVE. @ConfigurationProperties vincula un grupo de ajustes a una clase tipada, lo cual es más seguro que dispersar anotaciones @Value por todo el código base.

Validación

Las restricciones de Jakarta Bean Validation en un objeto de solicitud se aplican cuando el parámetro del controlador está anotado con @Valid.

public record CreatePostRequest(
        @NotBlank @Size(max = 120) String title,
        @NotBlank String body
) {
}

Un @RestControllerAdvice centraliza la respuesta de error para que cada endpoint devuelva la misma estructura.

@RestControllerAdvice
public class ApiExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, String> onValidation(MethodArgumentNotValidException ex) {
        return ex.getBindingResult().getFieldErrors().stream()
                .collect(Collectors.toMap(
                        FieldError::getField,
                        FieldError::getDefaultMessage,
                        (first, second) -> first));
    }
}

Valida en el límite y permite que el dominio confíe en sus entradas. Combinado con un manejador de excepciones consistente, esto elimina las comprobaciones repetitivas de if (input == null) en cada servicio.

Spring Security en breve

Spring Security es una cadena de filtros situada delante de tu aplicación. En las versiones modernas de Spring Boot, se configura mediante un bean en lugar de usar XML.

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/actuator/health").permitAll()
                        .anyRequest().authenticated())
                .oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults()));
        return http.build();
    }
}

Ese único bean requiere un JWT válido en cada endpoint, excepto en el de health check. Las reglas a nivel de método con @PreAuthorize añaden una autorización detallada donde sea necesario. La seguridad es un tema complejo: planea leer la documentación detenidamente antes de exponer cualquier cosa a internet.

Actuator: salud y métricas

Añadir el starter de Actuator expone endpoints operativos de forma gratuita.

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  endpoint:
    health:
      show-details: always
curl localhost:8080/actuator/health

/actuator/health se integra con las sondas de liveness y readiness, y /actuator/metrics reporta estadísticas de la JVM, HTTP y del datasource. Expón solo lo que necesites y protege el resto; estos endpoints revelan mucha información sobre un sistema en ejecución.

Pruebas

@SpringBootTest inicia el contexto de la aplicación y sirve como base para las pruebas de integración. MockMvc pone a prueba la capa web sin abrir un puerto real.

@SpringBootTest
@AutoConfigureMockMvc
class PostControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void listsPosts() throws Exception {
        mockMvc.perform(get("/api/posts"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].title").value("Hello"));
    }
}

Para obtener una respuesta más rápida, reduce el contexto únicamente a la capa que estás probando y utiliza mocks para los colaboradores.

@WebMvcTest(PostController.class)
class PostControllerSliceTest {

    @Autowired
    private MockMvc mockMvc;

    @MockitoBean
    private PostService service;

    @Test
    void returnsNotFound() throws Exception {
        given(service.findById(1L)).willThrow(new PostNotFoundException(1L));
        mockMvc.perform(get("/api/posts/1"))
                .andExpect(status().isNotFound());
    }
}

Utiliza slices para los controladores y repositorios, y reserva el contexto completo para unas pocas pruebas end-to-end. Testcontainers te permite tener una base de datos real en las pruebas sin necesidad de hacer mocking de la persistencia.

Construcción y despliegue

El proceso de build genera un executable fat jar que contiene tus clases, dependencias y el servidor embebido.

./mvnw clean package
java -jar target/blog-0.0.1-SNAPSHOT.jar

Para contenedores, Buildpacks crean una imagen optimizada sin necesidad de un Dockerfile.

./mvnw spring-boot:build-image

La configuración se gestiona a través del entorno, por lo que el mismo artefacto pasa de staging a producción sin cambios. Añade un Dockerfile solo cuando necesites un control que Buildpacks no ofrezca, y prefiere un multi-stage build para mantener la imagen ligera.

Mejores prácticas

  • Organiza los paquetes por funcionalidad (package by feature) para que cada dominio sea dueño de su propio controller, service y repository.
  • Utiliza inyección por constructor y mantén los campos final.
  • Devuelve DTOs desde los controllers en lugar de entidades JPA.
  • Valida los objetos de petición con @Valid y gestiona los errores en un único @RestControllerAdvice.
  • Mantén ddl-auto en validate y gestiona los cambios de esquema con Flyway o Liquibase.
  • Externaliza la configuración y utiliza perfiles en lugar de volver a compilar para cada entorno.
  • Añade Actuator, luego expón y asegura únicamente los endpoints que realmente necesites.

Errores comunes

  • Inyección de campos con @Autowired, lo que oculta las dependencias y complica las pruebas.
  • Serializar entidades directamente, provocando la filtración de columnas internas o de lazy-loading.
  • Permitir que los controladores acumulen lógica de negocio en lugar de delegarla a los servicios.
  • Usar ddl-auto: update en producción, lo que provoca una divergencia respecto al esquema real.
  • Iniciar todo el contexto en cada prueba unitaria, ralentizando la suite de pruebas.
  • Exponer todos los endpoints de Actuator a la internet pública.
  • Añadir starters que no necesitas y pagar el coste de rendimiento al iniciar la aplicación.

Próximos pasos

Spring Boot enseña inyección de dependencias, autoconfiguración y diseño por capas; conceptos que se aplican mucho más allá de Java. Si te gusta el modelo de módulos e inyección, NestJS lo implementa en TypeScript, mientras que Express muestra la alternativa minimalista. La capa web anotada es, en última instancia, una REST API, y la mayoría de las aplicaciones persisten los datos en un almacenamiento relacional, por lo que PostgreSQL es el siguiente paso natural.

Inyectar dependencias

La inyección por constructor hace explícitos los colaboradores requeridos, mantiene los campos como final y permite testear la clase sin iniciar Spring.

Preferir
@Service
public class PostService {

    private final PostRepository repository;

    public PostService(PostRepository repository) {
        this.repository = repository;
    }
}
Evitar
@Service
public class PostService {

    @Autowired
    private PostRepository repository;
    // hidden dependency, hard to test in isolation
}

Retornar datos desde controladores

Exponer entidades JPA acopla tu API al esquema. Mapea a un DTO para que los campos lazy, las columnas internas y las futuras migraciones permanezcan privados.

Preferir
@GetMapping("/{id}")
public PostResponse get(@PathVariable Long id) {
    return service.findById(id);
}
Evitar
@GetMapping("/{id}")
public Post get(@PathVariable Long id) {
    return repository.findById(id).orElseThrow();
    // serialising the entity leaks the schema
}

Compromisos

¿Vale la pena el peso de Spring Boot?

Spring Boot recompensa a los equipos que buscan una plataforma integrada y probada. El coste es el tiempo de arranque, la memoria y una gran superficie de aprendizaje.

Strengths

  • Todo está ya integrado

    Web, datos, seguridad, validación, mensajería y observabilidad comparten un único modelo de configuración, por lo que rara vez tienes que unir librerías manualmente.

  • El ecosistema y el mercado laboral son enormes

    Es el stack de Java empresarial por defecto. Las librerías lo toman como objetivo principal y es fácil encontrar desarrolladores experimentados.

  • Funciones de producción de primera clase

    Actuator, configuración externalizada, perfiles y métricas tratan las operaciones como una preocupación de diseño, no como algo secundario.

Trade-offs

  • Coste de arranque y memoria

    Un servicio de Spring Boot arranca más lento y usa más memoria que un framework minimalista. En serverless o en muchos servicios pequeños, esa carga es real.

  • La curva de aprendizaje es pronunciada

    La auto-configuración oculta mucho. Cuando el valor predeterminado es incorrecto, necesitas entender el contexto de Spring subyacente para depurarlo.

  • La magia de las anotaciones puede oscurecer el flujo

    El comportamiento se distribuye entre anotaciones, aspectos y proxies. Seguir una petición de principio a fin requiere práctica y buenas herramientas.

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender Spring Boot?

Nuestro tutorial interactivo te guia a traves de Spring Boot paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.