Spring Boot y Spring Cloud

Spring Boot explicado: starters, autoconfiguración y tu primera aplicación

Principiante10 min de lecturaActualizado

Esta página se tradujo del inglés con ayuda de IA y se cotejó con el original. Leer la versión en inglés

Al terminar esta guía entenderás qué añade Spring Boot sobre Spring Framework, cómo colaboran un starter y una clase de autoconfiguración, cómo leer el informe de evaluación de condiciones cuando falta un bean y cómo escribir tu primera aplicación con Boot 3 sin tratar el framework como si fuera magia.

Lo que aprenderás

  • Explicar qué añade Spring Boot sobre Spring Framework y qué deja igual
  • Describir cómo los starters, las clases de autoconfiguración y las condiciones producen beans durante el arranque
  • Crear y ejecutar una aplicación Spring Boot 3 con Maven y entender su punto de entrada
  • Leer el informe de evaluación de condiciones para averiguar por qué una autoconfiguración se aplicó o no
  • Sobrescribir o excluir una autoconfiguración de forma segura declarando tu propio bean

Spring Boot es hoy la forma en que se construye la mayoría de los servicios Java, y el motivo no es un nuevo modelo de programación. Boot deja Spring Framework exactamente como está y elimina lo que antes se comía la primera semana de un proyecto: elegir versiones de librerías compatibles, cablear los beans de infraestructura, configurar un contenedor de servlets y escribir el descriptor de despliegue. Añades una dependencia, Boot la detecta y aparece una configuración razonable. Esta página explica la maquinaria que hay detrás para que puedas predecir lo que hará Boot, averiguar por qué hizo otra cosa y sobrescribirlo cuando su opinión no coincide con la tuya. Todo lo que sigue se refiere a Spring Boot 3 sobre Java 17 o posterior.

Qué añade Boot y qué deja intacto

Spring Framework te da un contenedor de inversión de control, programación orientada a aspectos, gestión de transacciones, Spring MVC y una larga lista de integraciones. Lo que deliberadamente no te da es una opinión sobre cómo ensamblarlos. Por eso, una aplicación Spring clásica declaraba a mano, en XML o en configuración Java, un DataSource, un EntityManagerFactory, un gestor de transacciones, un DispatcherServlet, convertidores de mensajes y un resolvedor de vistas, y empaquetaba todo como un WAR para un servidor de aplicaciones externo.

Spring Boot aporta cuatro cosas sobre esa base.

Gestión de dependencias. El BOM (bill of materials) spring-boot-dependencies fija la versión de varios cientos de librerías que se sabe que funcionan juntas. Declaras spring-boot-starter-web sin versión y heredas un conjunto coherente.

Starters. Un starter es un artefacto vacío de Maven o Gradle cuya única función es traer un grupo seleccionado de dependencias para una capacidad concreta: web, datos, seguridad, validación, testing.

Autoconfiguración. Un conjunto de clases @Configuration que registran beans de infraestructura solo cuando se cumplen las condiciones adecuadas: la librería está en el classpath, no existe ningún bean de usuario de ese tipo, una propiedad está definida.

Funcionalidades de producción. Un servidor servlet o reactivo embebido para que la aplicación se ejecute con java -jar, configuración externalizada y el módulo Actuator para endpoints de salud, métricas y gestión.

Nada de esa lista cambia la forma de escribir un servicio, un repositorio o un controlador. Si sabes leer una aplicación Spring normal, sabes leer una aplicación Boot; la diferencia está en cuánta configuración de infraestructura es visible en el código fuente.

Una primera aplicación

Genera un proyecto con Spring Initializr (la web, la CLI spring o el asistente integrado en IntelliJ IDEA, Eclipse y VS Code) con la dependencia Web. Con Maven obtienes un pom.xml cuyo padre es spring-boot-starter-parent:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.5.5</version>
</parent>

<properties>
  <java.version>21</java.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

El POM padre aporta el BOM, ajustes sensatos para el compilador y el filtrado de recursos, y el plugin que reempaqueta el build como un jar ejecutable. Si tu organización ya tiene un POM padre corporativo, importa spring-boot-dependencies en dependencyManagement con <scope>import</scope>; pierdes los valores por defecto del plugin, pero mantienes la alineación de versiones.

El punto de entrada es una sola clase:

package com.example.shop;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class ShopApplication {

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

@RestController
class HelloController {

    @GetMapping("/hello")
    String hello() {
        return "Hello from Spring Boot";
    }
}

Ejecuta ./mvnw spring-boot:run (o java -jar target/shop-0.0.1-SNAPSHOT.jar tras ./mvnw package) y Tomcat responde en el puerto 8080. No declaraste el contenedor de servlets, ni el DispatcherServlet, ni el convertidor JSON de Jackson, ni la página de error: lo hizo la autoconfiguración, porque spring-boot-starter-web puso las clases necesarias en el classpath.

@SpringBootApplication es la composición de tres anotaciones:

AnotaciónEfecto
@SpringBootConfigurationMarca la clase como @Configuration y permite que los tests la localicen automáticamente
@EnableAutoConfigurationActiva el mecanismo de autoconfiguración descrito más abajo
@ComponentScanEscanea el paquete de esta clase y sus subpaquetes en busca de @Component, @Service, @Repository, @Controller y @Configuration

El detalle del escaneo de componentes tiene una consecuencia práctica: coloca la clase de la aplicación en tu paquete raíz (com.example.shop), no en el paquete por defecto ni en un subpaquete como com.example.shop.config. Las clases que quedan fuera del árbol escaneado se ignoran sin avisar, y esa es una de las sorpresas más habituales del tipo «no encuentra mi bean».

Starters: paquetes de dependencias con una convención de nombres

Abre spring-boot-starter-web en la vista de dependencias de tu IDE y no encontrarás código, solo dependencias transitivas: spring-boot-starter (el núcleo, el logging y el soporte YAML), spring-boot-starter-json (Jackson), spring-boot-starter-tomcat y los módulos del framework spring-web/spring-webmvc. Los starters existen para que «quiero construir una aplicación web» sea una línea en lugar de ocho, y para que cambiar de tecnología sea cuestión de exclusiones: excluye spring-boot-starter-tomcat, añade spring-boot-starter-jetty y el servidor embebido cambia sin tocar el código.

Los starters oficiales se llaman spring-boot-starter-*. La documentación de referencia pide a terceros que usen el patrón inverso, acme-spring-boot-starter, para que el prefijo spring-boot-starter- quede reservado al equipo de Spring. Esa convención te dice de un vistazo si un starter lo mantiene el propio proyecto o una comunidad.

Algunos starters que conviene conocer pronto:

StarterQué aporta
spring-boot-starter-webSpring MVC, Jackson, Tomcat embebido
spring-boot-starter-webfluxStack web reactivo sobre Reactor Netty
spring-boot-starter-data-jpaSpring Data JPA, Hibernate, pool de conexiones HikariCP
spring-boot-starter-validationJakarta Bean Validation con Hibernate Validator
spring-boot-starter-securitySpring Security con una configuración segura por defecto
spring-boot-starter-actuatorEndpoints de salud, métricas y gestión
spring-boot-starter-testJUnit 5, AssertJ, Mockito, Spring Test, aserciones JSON

Fíjate en que spring-boot-starter-validation está separado de -web. Esa división llegó con Boot 2.3; si @Valid parece no hacer nada en un proyecto recién creado, la causa suele ser que falta ese starter.

Cómo funciona la autoconfiguración

La autoconfiguración es configuración Java de Spring normal y corriente, con dos añadidos: un fichero de registro que enumera las clases candidatas y condiciones que deciden si cada una se aplica.

Registro

Desde Boot 2.7, y de forma exclusiva desde la 3.0, las clases de autoconfiguración se enumeran, un nombre totalmente cualificado por línea, en un recurso llamado META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports dentro del jar que las proporciona. La antigua clave de spring.factories para la autoconfiguración se eliminó en la 3.0; una librería que todavía dependa de ella no se detectará. @EnableAutoConfiguration lee todos esos ficheros del classpath e importa las clases que encuentra. Solo el jar spring-boot-autoconfigure incluye bastante más de un centenar.

Condiciones

Cada clase, y a menudo cada método @Bean que contiene, lleva condiciones del paquete org.springframework.boot.autoconfigure.condition:

CondiciónSe aplica cuando
@ConditionalOnClass / @ConditionalOnMissingClassUna clase está (o no está) en el classpath
@ConditionalOnBean / @ConditionalOnMissingBeanYa existe (o no existe) un bean de un tipo o de un nombre dado
@ConditionalOnPropertyUna propiedad tiene un valor concreto, o no existe y se usa matchIfMissing
@ConditionalOnWebApplication / @ConditionalOnNotWebApplicationLa aplicación es (o no es) una aplicación web servlet o reactiva
@ConditionalOnResourceExiste un recurso, como un fichero, en el classpath
@ConditionalOnExpressionUna expresión SpEL se evalúa como true

@ConditionalOnMissingBean es la que hace que Boot sea educado. Las clases de autoconfiguración se procesan después de tus propias clases @Configuration, así que, en cuanto declaras un bean de un tipo que Boot crearía, la versión de Boot se retira. Por eso el consejo habitual para personalizar Boot es «define el bean tú mismo» y no «desactiva el bean de Boot».

Esta es una versión simplificada de la autoconfiguración del data source, escrita como escribirías la tuya:

@AutoConfiguration
@ConditionalOnClass(DataSource.class)          // only if JDBC support is present
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean                  // back off if the app declared one
    @ConditionalOnProperty(prefix = "spring.datasource", name = "url")
    DataSource dataSource(DataSourceProperties properties) {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl(properties.getUrl());
        ds.setUsername(properties.getUsername());
        ds.setPassword(properties.getPassword());
        return ds;
    }
}

@AutoConfiguration (introducida en la 2.7) sustituye a @Configuration en estas clases. Desactiva el proxy de los métodos @Bean, que la autoconfiguración no necesita, y acepta los atributos before/after para ordenarse respecto a otras autoconfiguraciones. El orden importa porque una condición como @ConditionalOnBean solo tiene sentido si el bean que busca ha tenido ocasión de registrarse antes.

Escribir la tuya

Los equipos que mantienen varios servicios suelen acabar con una librería compartida que configura los aspectos comunes de la misma forma en todas partes: un ObjectMapper ajustado, un cliente HTTP con cabeceras de tracing, un gestor de errores estándar. Empaquetar eso como autoconfiguración en lugar de como clase base deja a los servicios que la consumen libres de sobrescribir cualquier pieza:

acme-http-spring-boot-starter/
└── src/main/resources/META-INF/spring/
    └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
        (contains: com.acme.http.AcmeRestClientAutoConfiguration)
@AutoConfiguration
@ConditionalOnClass(RestClient.class)
public class AcmeRestClientAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    RestClient.Builder acmeRestClientBuilder() {
        return RestClient.builder()
                .defaultHeader("X-Acme-Caller", "shop");
    }
}

Mantén las clases de autoconfiguración fuera del alcance de @ComponentScan de las aplicaciones que las usan; si un escaneo las recoge como clases @Configuration normales, se pierde la garantía de orden y @ConditionalOnMissingBean puede comportarse de forma inesperada. Basta con colocar la librería en su propia jerarquía de paquetes (com.acme.http, no com.example.shop.*).

Averiguar qué ha pasado

La autoconfiguración es determinista, pero no siempre evidente, y Boot te ofrece dos maneras de ver sus decisiones.

Arranca la aplicación con --debug (o debug=true en application.properties) y lee el informe de evaluación de condiciones en el log. Tiene tres partes: coincidencias positivas (autoconfiguraciones que se aplicaron, con la condición que las dejó pasar), coincidencias negativas (las que no, con la condición que falló) y exclusiones. Una entrada negativa típica tiene este aspecto:

DataSourceAutoConfiguration:
   Did not match:
      - @ConditionalOnClass did not find required class 'javax.sql.DataSource'

Esa línea te dice exactamente qué dependencia añadir. Cuando falta un bean sin explicación, este informe responde a la pregunta más rápido que leer el código fuente.

Si el starter de Actuator está presente, la misma información está disponible en tiempo de ejecución a través del endpoint conditions una vez que lo expones, algo muy útil en entornos donde no puedes reiniciar con un flag.

Sobrescribir y excluir

Hay tres niveles de control, del más suave al más drástico.

  1. Define una propiedad. La mayoría de las autoconfiguraciones se apoyan en clases @ConfigurationProperties, así que server.port, spring.datasource.url o spring.jackson.default-property-inclusion cambian el comportamiento sin tocar código. La lista completa está en el apéndice «Common Application Properties» de la documentación de referencia.
  2. Declara el bean tú mismo. Gracias a @ConditionalOnMissingBean, tu bean sustituye al de Boot. Si necesitas el bean de Boot más un ajuste, busca un punto de extensión de tipo customizer: WebMvcConfigurer, Jackson2ObjectMapperBuilderCustomizer, RestClientCustomizer, TomcatConnectorCustomizer y muchos otros existen precisamente para ajustar sin sustituir.
  3. Excluye la autoconfiguración. @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) o la propiedad spring.autoconfigure.exclude la eliminan por completo. Úsalo cuando la librería esté en el classpath por un motivo ajeno a la funcionalidad que Boot deduce, por ejemplo un driver JDBC que solo usa una herramienta en tiempo de build.

Recurre al nivel 3 en contadas ocasiones. Una autoconfiguración excluida suele tener dependientes (DataSourceAutoConfiguration alimenta a JPA, a las plantillas JDBC, a Flyway y a los indicadores de salud), y excluir una puede desencadenar una cadena de beans ausentes más difícil de depurar que el problema original.

Errores comunes

Clase de aplicación en el paquete equivocado. Los componentes de un paquete hermano o padre nunca se escanean. Mueve la clase al paquete raíz o añade scanBasePackages a @SpringBootApplication; lo primero es más limpio.

Pelearse con el POM padre. Sobrescribir una versión gestionada (por ejemplo, forzar un Hibernate más reciente) es legítimo, pero debe hacerse mediante la propiedad que expone el BOM (<hibernate.version>) y no declarando una versión en la dependencia, para que se mantenga la alineación de las transitivas. Lee antes las notas de la versión de la librería: la versión del BOM es aquella con la que se probó la autoconfiguración de Boot.

Suponer que un starter equivale a una funcionalidad. Añadir spring-boot-starter-data-jpa sin un driver de base de datos ni un spring.datasource.url hace fallar el arranque con un mensaje sobre una base de datos embebida. Boot solo puede configurar lo que describen el classpath y las propiedades.

Mezclar javax.* y jakarta.*. Una aplicación Boot 3 con una dependencia que todavía usa javax.servlet o javax.persistence compila, pero falla en tiempo de ejecución con NoClassDefFoundError o con entidades sin mapear. Comprueba que cada librería de terceros tenga una versión compatible con Jakarta antes de actualizar.

Tratar --debug como un nivel de log. El flag activa el informe de condiciones y el logging DEBUG de unos pocos loggers del núcleo, no el de tus propios paquetes; para eso usa logging.level.com.example=DEBUG.

Ideas clave

  • Spring Boot es Spring Framework más gestión de dependencias, starters, autoconfiguración y funcionalidades de producción; el modelo de programación no cambia.
  • Un starter es un paquete de dependencias; una autoconfiguración es una clase @Configuration condicional registrada en META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
  • Condiciones como @ConditionalOnClass y @ConditionalOnMissingBean hacen que los beans de Boot se retiren en cuanto declaras los tuyos, que es la forma prevista de personalizarlo.
  • @SpringBootApplication combina configuración, autoconfiguración y escaneo de componentes desde el paquete de la clase hacia abajo, así que su ubicación importa.
  • Cuando falta un bean o aparece uno inesperado, el informe de evaluación de condiciones (--debug, o el endpoint conditions de Actuator) te dice qué condición lo decidió.
  • Prefiere las propiedades y los beans customizer a excluir autoconfiguraciones; las exclusiones tienen efecto en cascada.

Fuentes y lecturas recomendadas

Preguntas frecuentes

¿Spring Boot es un framework distinto de Spring?
No. Spring Boot es una capa sobre Spring Framework. Todos los beans, anotaciones y abstracciones que usas en una aplicación Boot (inyección de dependencias, @Transactional, Spring MVC, Spring Data) proceden del framework y de los proyectos de su ecosistema. Boot añade una gestión de dependencias con criterio propio, autoconfiguración, un servidor embebido y funcionalidades de producción como Actuator.
¿Por qué Spring Boot 3 exige Java 17 y los paquetes jakarta.*?
Spring Boot 3 se basa en Spring Framework 6, que fijó Java 17 como versión mínima y pasó de Java EE a Jakarta EE 9+. Jakarta EE 9 renombró todos los paquetes de API javax.* a jakarta.*, así que cambian los imports de servlets, persistencia, validación y otras API. Las librerías que todavía distribuyen clases javax.* no funcionarán con Boot 3 hasta que se actualicen.
¿Cómo sé qué autoconfiguraciones se aplicaron a mi aplicación?
Arranca la aplicación con la propiedad debug activada (por ejemplo, --debug en la línea de comandos o debug=true en application.properties). Boot escribe en el log un informe de evaluación de condiciones con las coincidencias positivas, las negativas y la condición exacta que decidió cada una. Con Actuator puedes consultar esos mismos datos en tiempo de ejecución a través del endpoint conditions.
Si defino mi propio bean DataSource, ¿Boot creará otro de todos modos?
No. DataSourceAutoConfiguration está protegida por @ConditionalOnMissingBean, y la configuración del usuario se procesa antes que la autoconfiguración. Tu bean gana y el autoconfigurado se omite. Este repliegue es la forma estándar de personalizar Boot: define el bean tú mismo en lugar de pelearte con los valores por defecto.
¿La autoconfiguración ralentiza el arranque?
Cada clase de autoconfiguración se evalúa al arrancar, pero la mayoría se descarta casi al instante mediante una comprobación @ConditionalOnClass muy barata que falla antes incluso de cargar la clase. Lo que de verdad pesa en el tiempo de arranque es el trabajo de los beans que sí se crean (abrir pools de conexiones, construir los metamodelos de Hibernate, arrancar Tomcat), no la evaluación de condiciones en sí.