En pocas palabras
Proyecto del ecosistema Spring que ofrece autoconfiguración, starters de dependencias, servidor embebido y funciones operativas (Actuator, métricas, health checks) para construir aplicaciones Java y Kotlin.
Qué problema resuelve
Configuración extensa y repetitiva para arrancar un servicio Java con web, datos, seguridad y observabilidad.
Cuándo utilizarlo
Servicios backend en la JVM, APIs REST, consumidores de eventos y jobs. Ejemplo: la API de solicitudes que integra PostgreSQL, Kafka y un buró de crédito.
De la idea a la práctica
Funcionamiento, ejemplos y decisiones para pasar al siguiente nivel.
Piezas principales
| Pieza | Qué aporta |
|---|---|
| Starters | Conjuntos coherentes de dependencias (spring-boot-starter-web, -data-jpa) |
| Autoconfiguración | Beans creados según lo que hay en el classpath y la configuración |
| Configuración externa | application.yml, variables de entorno y perfiles, con @ConfigurationProperties tipadas |
| Actuator | Health, métricas (Micrometer), info, sondas de Kubernetes |
| Testing | @SpringBootTest, slices (@WebMvcTest, @DataJpaTest) y Testcontainers |
Configuración tipada
@ConfigurationProperties(prefix = "buro")
public record BuroProperties(URI baseUrl, Duration timeout, int maxConcurrentes) {}
buro:
base-url: https://api.buro.example.com
timeout: 3s
max-concurrentes: 20
management:
endpoints.web.exposure.include: health,info,prometheus
endpoint.health.probes.enabled: true
Cada propiedad se puede sobrescribir con una variable de entorno (BURO_TIMEOUT=5s), lo que encaja con contenedores y Cloud Run.
Hilos virtuales
Con Java 21 o superior, spring.threads.virtual.enabled=true hace que el servidor web y los ejecutores usen hilos virtuales. Para servicios con mucha E/S bloqueante (JDBC, HTTP a terceros) permite alta concurrencia sin adoptar programación reactiva. Vigila los límites de las dependencias: miles de hilos virtuales pueden agotar un pool de 10 conexiones si no hay un bulkhead.
Pruebas con Testcontainers
@SpringBootTest
@Testcontainers
class SolicitudRepositoryTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:17");
@Autowired SolicitudRepository repo;
@Test
void guardaYRecupera() { /* … */ }
}
@ServiceConnection configura el datasource automáticamente a partir del contenedor, sin propiedades manuales.
Arranque y memoria en serverless
| Opción | Efecto |
|---|---|
| Lazy initialization | Arranque más rápido, errores de configuración más tardíos |
| CDS / AOT cache (JDK) | Reduce el tiempo de arranque reutilizando clases precargadas |
| GraalVM native image | Arranque en milisegundos y menos memoria; build más lento y restricciones de reflexión |
-XX:MaxRAMPercentage=75 |
Ajusta el heap al límite del contenedor |
API protegida con JWT
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://idp.krediya.example/realms/krediya
@Configuration
class Seguridad {
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(a -> a
.requestMatchers("/actuator/health/**").permitAll()
.requestMatchers(HttpMethod.POST, "/v1/solicitudes/*/aprobar").hasAuthority("SCOPE_creditos:aprobar")
.anyRequest().authenticated())
.oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()))
.build();
}
}
Las autorizaciones por recurso («este asesor solo ve su sucursal») van además en el servicio de aplicación o con @PreAuthorize, no solo en la configuración de rutas.
Errores con Problem Details (RFC 9457)
@RestControllerAdvice
class Errores extends ResponseEntityExceptionHandler {
@ExceptionHandler(ReglaDeNegocioVioladaException.class)
ProblemDetail regla(ReglaDeNegocioVioladaException e) {
var p = ProblemDetail.forStatusAndDetail(HttpStatus.UNPROCESSABLE_ENTITY, e.getMessage());
p.setType(URI.create("https://api.krediya.example/errores/regla-negocio"));
p.setProperty("codigo", e.codigo());
return p;
}
}
Un formato de error estándar facilita a los clientes y a la observabilidad tratar todos los servicios igual.
Observabilidad y apagado ordenado
server:
shutdown: graceful # termina las peticiones en curso al recibir SIGTERM
spring:
lifecycle:
timeout-per-shutdown-phase: 20s
management:
tracing:
sampling:
probability: 0.2
otlp:
tracing:
endpoint: http://otel-collector:4318/v1/traces
observations:
key-values:
servicio: api-solicitudes
logging:
structured:
format:
console: ecs # logs JSON estructurados
Micrometer genera métricas y trazas (con exportación OTLP) para peticiones HTTP, JDBC, clientes HTTP y Kafka sin código adicional.
Imagen de contenedor
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=us-east1-docker.pkg.dev/krediya/apps/api:1.9.0
Los Cloud Native Buildpacks generan una imagen con JRE ajustado, usuario no root y capas separadas (dependencias, código), sin escribir un Dockerfile. Si se prefiere un Dockerfile, usa el layered jar (java -Djarmode=tools -jar app.jar extract --layers) para aprovechar la caché de capas.
Mantener la versión al día
Cada versión menor de Spring Boot tiene un periodo de soporte open source limitado; planifica al menos una actualización menor al año. El orden recomendado: subir a la última patch de la versión actual, resolver avisos de deprecación, y luego saltar de versión con ayuda de OpenRewrite y de las notas de migración oficiales.
Criterios de implementación
Genera proyectos desde Spring Initializr con una plantilla corporativa (starters internos para logging, seguridad y trazas). Expón solo los endpoints de Actuator necesarios y protégelos. Organiza el código por bounded context, protege la estructura con fitness functions (ArchUnit) y añade Resilience4j en las integraciones externas.
Continúa en las fuentes
Documentación oficial para verificar detalles y explorar más.