CodeGym /Cursos /Spring Boot /Initializr: estructura y dependencias starter

Initializr: estructura y dependencias starter

Spring Boot
Nivel 3, Lección 3
Disponible

1. El sentido de analizar el skeleton antes del código

Cuando el principiante abre por primera vez un proyecto Spring Boot, suele ver no un «esqueleto cuidado», sino un «bosque de carpetas». Es una reacción normal: el cerebro reconoce honestamente que todavía no sabe distinguir lo importante de lo secundario. En esta lección haremos el proyecto legible para que dejes de temer la estructura y empieces a usarla como una pista, no como una prueba de fe.

Tras build.gradle.kts el panorama sigue incompleto: las reglas del build ya son claras, pero todavía no se ve en qué se convierten en el disco. Por eso ahora literalmente «abriremos la caja» del esqueleto generado: carpetas, clases, recursos y starters.

La idea más útil: Initializr genera el proyecto no porque «así es costumbre», sino porque Boot y Gradle se apoyan en convenciones. Si el proyecto cumple esas convenciones, las herramientas entienden dónde buscar el código, dónde están los recursos, dónde están los tests, cómo lanzar el build y cómo empaquetar el resultado.

Es conveniente tener en mente un esquema simple: en cualquier proyecto Gradle normal (y en un proyecto Boot también) los archivos se dividen en varias zonas de responsabilidad.

flowchart TD
  A[Proyecto catalog-service] --> B[Infraestructura de build]
  A --> C[Código de la aplicación]
  A --> D[Recursos]
  A --> E[Tests]

  B --> B1[gradlew / gradle/]
  B --> B2[build.gradle.kts / settings.gradle.kts]
  C --> C1[src/main/java]
  D --> D1[src/main/resources]
  E --> E1[src/test/java]

Mientras no aprendes a «leer» ese mapa, el IDE parece un armario donde todo está mezclado: los calcetines están junto a las cacerolas, y el pasaporte entre las bolsas del supermercado. En cuanto recuerdas las zonas, el proyecto se convierte en un piso comprensible: la cocina está en la cocina, el dormitorio en el dormitorio, y el gato... bueno, el gato de todos modos está en algún lugar en el teclado, pero al menos sabes la distribución.

2. Mapa del proyecto: la raíz del repositorio

La raíz del proyecto es el lugar donde el principiante normalmente empieza a entrar en pánico: «¿Por qué hay tantos archivos, si todavía no escribí ni una línea de código de negocio?» Con calma: la mayoría de los archivos en la raíz no son código de la aplicación, sino instrucciones de cómo ensamblar y lanzar el proyecto. Es como la caja de un dispositivo: todavía no encendiste el aparato, pero ya hay pasaporte, garantía y cargador.

En un proyecto típico catalog-service creado a través de Initializr bajo Gradle, verás aproximadamente esta estructura:

catalog-service/
├─ build.gradle.kts
├─ settings.gradle.kts
├─ gradlew
├─ gradlew.bat
├─ gradle/
│  └─ wrapper/
│     ├─ gradle-wrapper.jar
│     └─ gradle-wrapper.properties
└─ src/
   ├─ main/
   └─ test/

Ordenemos esto por sentido. La tabla a continuación no es para «aprenderlo todo», sino para que te acostumbres: cada archivo en la raíz cumple su rol.

Lo que ves Qué es Por qué importa
build.gradle.kts script principal de build de Gradle describe las dependencias, los plugins y el build del proyecto
settings.gradle.kts ajustes del proyecto Gradle como mínimo: el nombre del proyecto (root project name)
gradlew /
gradlew.bat
scripts Gradle Wrapper lanzamiento de la versión correcta de Gradle desde el proyecto
gradle/wrapper/* «relleno» del Wrapper aquí está fijada la versión de Gradle y la forma de descargarlo
src/ fuentes ya es el «territorio de la aplicación»: código, recursos, tests

Psicológicamente importante: la raíz del proyecto es ante todo infraestructura, no lógica de negocio. Eso no significa que los archivos sean «no importantes». Significa que no estás obligado a entenderlos ahora al nivel de un arquitecto de plugins de Gradle. Basta con entender que sin ellos el proyecto pierde reproducibilidad.

De referencia, aquí hay un ejemplo de settings.gradle.kts mínimo que normalmente se genera:

// Nombre del proyecto raíz: lo mostrará el IDE, y a menudo se usa en el build
rootProject.name = "catalog-service"

Un archivo, una línea — y de todos modos es útil: de ella se obtendrá el nombre del módulo en el IDE y el nombre base del artefacto (y también simplemente se lee bien).

3. La carpeta src: el separador principal «código / recursos / tests»

La carpeta src es el lugar donde el programador principiante normalmente suspira de alivio: «Oh, por fin algo que parece código». Pero también aquí es importante no convertir el proyecto en un vertedero. El sentido principal de src es que Gradle y los proyectos Java ya llevan mucho tiempo acordados sobre la distribución estándar: dónde está el código, dónde están los recursos, dónde están los tests. Ese acuerdo te ahorra una masa de ajustes manuales.

Dentro de src casi siempre verás dos niveles superiores: main y test. Eso no es «un capricho de Spring», sino un estándar general de ingeniería: la aplicación y los tests se separan físicamente.

src/
├─ main/
│  ├─ java/
│  └─ resources/
└─ test/
   ├─ java/
   └─ resources/

src/test/resources puede aparecer más adelante — cuando los tests necesiten archivos y datos (JSON, scripts SQL, fixtures, etc.).

El sentido es este: todo lo que está en src/main pertenece a la aplicación «de producción», es decir, a lo que al final se lanza. Todo lo que está en src/test pertenece al código de test, es decir, a lo que ayuda a verificar esa aplicación.

Un pensamiento «prosaico» más útil: Gradle no intenta adivinar dónde tienes el código Java. Simplemente mira en las carpetas estándar. Por eso si de repente pones una clase Java en src/main/resources, no se convierte en un «recurso con inteligencia», sino en un problema.

Para recordarlo más fácilmente, puedes tener en mente una tabla corta:

Ruta Qué hay allí Cómo pensarlo
src/main/java fuentes de la aplicación (Java) «lo que se compila»
src/main/resources recursos (no Java) «lo que se pone junto a la aplicación»
src/test/java tests en Java «lo que verifica la aplicación»
src/test/resources recursos para tests «datos/archivos que necesitan los tests»

El sentido de esa separación se volverá especialmente agradable cuando el proyecto crezca. Ahora parece burocracia. Luego se siente como el orden en el escritorio: cuando sabes dónde está el cargador, no te sientes arqueólogo cada noche.

4. src/main/java: el paquete base y la clase principal de la aplicación

src/main/java es la parte más «viva» del proyecto: aquí estará tu código aplicativo. Pero también aquí Initializr no crea caos, sino que de inmediato da un buen comienzo: crea el paquete base y la clase principal. En este punto es especialmente importante entender que el paquete en Java no es simplemente «una cadena en package ...;», sino también la estructura de carpetas, y las «fronteras de visibilidad» para el escaneo de componentes.

Si elegiste, por ejemplo, group = com.example y artifact = catalog-service, entonces Initializr normalmente ensambla el paquete base como:

com.example + catalogservice  →  com.example.catalogservice

Y entonces la ruta hasta la clase principal será esta:

src/main/java/com/example/catalogservice/CatalogServiceApplication.java

Por dentro — un punto de entrada Boot completamente mínimo: una clase con main() y la anotación que le dice a Boot: «Desde aquí empezamos».

package com.example.catalogservice;

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

// Clase principal de la aplicación: exactamente desde aquí Spring Boot arranca y levanta el contexto
@SpringBootApplication
public class CatalogServiceApplication {

    public static void main(String[] args) {
        // Lanzamiento de la aplicación Spring Boot (se levanta el contexto, la autoconfiguración, el servidor embebido, etc.)
        SpringApplication.run(CatalogServiceApplication.class, args);
    }
}

Ahora lo que nos importa no es «analizar la magia de @SpringBootApplication hasta los huesos» (eso será un tema aparte del curso), sino ver el propósito del archivo: es el punto de entrada de la aplicación, desde donde arranca. En términos de Java ordinario eso es similar al método principal de un programa de consola, solo que aquí levanta todo un runtime y un contenedor.

Otro momento importante de nuestra repetición de Spring Core: la ubicación de esa clase en el paquete importa. Muy en términos generales (y de momento sin detalles) puede pensarse así: Spring Boot mira el paquete de la clase principal y lo considera la «raíz» para buscar tus componentes. Por eso el buen hábito es mantener la clase principal en el paquete superior ordenado del proyecto, y no esconderla en algún lugar de las profundidades como com.example.catalogservice.utils.startup.launcher.finalversion2.

Si luego añades subpaquetes (por ejemplo, catalog, config, etc.), quedarán lógicamente «por debajo» del paquete base, y el proyecto se leerá de forma natural.

5. src/test/java: el esqueleto de test que aparece de inmediato

La carpeta src/test/java en muchos principiantes provoca sorpresa: «Si todavía no estoy probando nada, ¿por qué ya me dieron un test?» En realidad es una pista de ingeniería muy correcta de Initializr: un buen servicio no es solo código, sino también una verificación mínima de que al menos puede arrancar. Aunque todavía no estés listo para pensar en la estrategia de testing, es útil saber que el esqueleto ya existe.

Initializr normalmente crea el test en el mismo paquete que la clase main:

src/test/java/com/example/catalogservice/CatalogServiceApplicationTests.java

Aquí está el típico test «aparentemente vacío» que te van a generar:

package com.example.catalogservice;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

// Test de integración: levanta el contexto Spring y comprueba que la aplicación en general arranca
@SpringBootTest
class CatalogServiceApplicationTests {

    @Test
    void contextLoads() {
        // El cuerpo puede estar vacío: si el contexto no se levanta, el test fallará antes de ejecutar este método
        // En el curso consideramos que esto se lanza en JUnit 6 (la anotación @Test tiene el mismo sentido)
    }
}

Desde el punto de vista del principiante esto parece casi una broma: el método está vacío, pero el archivo existe. Pero el sentido está precisamente en que si el contexto de la aplicación no pudo levantarse, el test fallará antes de que empiece a ejecutar tu código dentro de contextLoads(). Es decir, ese test responde a la pregunta: «¿El proyecto en general está vivo? ¿Compila? ¿Al menos arranca a nivel de contexto?»

Hoy no profundizamos en los detalles de @SpringBootTest, JUnit 6 y los enfoques de testing — simplemente registramos: los tests aparecen no porque «así manda la metodología», sino porque el esqueleto del proyecto debe tener un mínimo de cable de seguridad. Aunque todavía no seas alpinista, el cable ya está enganchado — y eso es bueno.

6. src/main/resources: recursos y disciplina

src/main/resources es el lugar donde vive todo lo que la aplicación necesita pero que no es código Java. En los principiantes aquí hay dos extremos: unos ignoran esa carpeta («hay algo mágico»), otros empiezan a meter allí todo lo que cae («bueno, son simplemente archivos»). La realidad está en el medio: resources es una capa importante de la aplicación, pero con su propia disciplina.

El proyecto generado típico contiene el archivo de configuración de la aplicación. En el proyecto generado puedes ver aquí application.properties — es la variante habitual por defecto en Initializr, no una segunda línea igual del proyecto. En catalog-service nos mantenemos en YAML-first, por lo que el formato de trabajo consideramos application.yaml, y no una mezcla de dos formatos.

src/main/resources/
└─ application.yaml

El contenido mínimo puede verse así:

spring:
  application:
    name: catalog-service

Si tras la generación tienes application.properties, no es un problema: lo importante es la propia carpeta resources y el lugar donde viven los ajustes.

La idea de la carpeta resources es que su contenido entra en el classpath de la aplicación. Dicho de forma más sencilla, son archivos que la aplicación podrá «ver» durante el funcionamiento. Más adelante aquí pueden estar, por ejemplo, archivos estáticos, configs, plantillas, localizaciones — pero hoy lo importante es simplemente aprender a distinguir el código de los recursos y no confundir las carpetas.

El error más frecuente del principiante aquí se ve así: crea una clase Java en resources, porque «en el IDE es más cómodo». Luego Gradle no la compila, Spring no la ve, y el estudiante piensa que Spring se rompió. En realidad Spring no se rompió — simplemente no está obligado a buscar código Java donde según el estándar están los recursos.

7. Dependencias starter en el skeleton

Tras el mapa de carpetas surge naturalmente la pregunta: ¿por qué ese esqueleto ya parece vivo aunque en dependencies haya pocas líneas? La respuesta está precisamente en los starters.

Cuando por primera vez miras build.gradle.kts, puedes experimentar una extraña sensación: «Tengo solo dos líneas de dependencias, y el proyecto parece un servidor completo. ¿Dónde está el truco?» No hay truco: es la filosofía de Spring Boot. En lugar de obligar al principiante a ensamblar la infraestructura pieza a pieza, Boot ofrece entradas a través de starters — conjuntos listos de dependencias para un escenario típico.

En el formulario de Initializr esa dependencia puede esconderse bajo el nombre Spring Web, pero en build.gradle.kts la línea canónica para el curso sigue siendo org.springframework.boot:spring-boot-starter-webmvc.

dependencies {
    // El starter web base del curso: trae Spring MVC, el servidor embebido y todo lo típico para una aplicación HTTP
    implementation("org.springframework.boot:spring-boot-starter-webmvc")

    // El starter de test: infraestructura de test Spring + JUnit 6
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

Ahora lo importante es solo entender el sentido, sin meterse en los detalles de la composición interna. spring-boot-starter-webmvc es el «billete de entrada» al mundo web (parte del servidor, infraestructura MVC y todo lo necesario para que la aplicación pueda funcionar como servicio HTTP). spring-boot-starter-test es el «billete de entrada» análogo al mundo de tests (JUnit 6 e infraestructura de test útil).

Fíjate en algo psicológicamente importante: conectaste un starter, no «una biblioteca». Por eso hay pocas líneas, y el efecto es grande. No es magia, sino un conjunto de dependencias seleccionado de antemano. Y esa es una de las razones por las que los proyectos Boot arrancan tan agradablemente: describes el escenario («necesito web»), y la plataforma tira el conjunto base de piezas.

También puedes notar que las dependencias normalmente no tienen versión indicada. Eso es normal para el enfoque Boot: Initializr y el plugin Boot están configurados para que el proyecto se mantenga en una línea unificada y concordada de bibliotecas. Hoy es suficiente con registrar simplemente: «la versión no siempre se necesita en la línea de dependencia», y no intentar «curar» eso añadiendo cifras aleatorias :1.2.3 «por si acaso».

8. Mini-verificación: qué está ya listo

En este momento es útil respirar un poco y reconocer: ya tienes un proyecto, aunque casi no escribiste código. Pero el proyecto está intencionadamente vacío de funcionalidad. Es como un cuaderno nuevo: las páginas existen, el bolígrafo existe, el índice existe, pero la novela todavía no está escrita. Y eso es bueno — queremos empezar desde un esqueleto claro, no desde el caos.

Si describes el estado de catalog-service tras Initializr en un párrafo, resulta así: en la raíz está la infraestructura de build (Gradle y Wrapper), en src/main/java está el punto de entrada de la aplicación, en src/test/java está el test mínimo «el contexto arranca», y en src/main/resources hay un lugar para los recursos y la configuración. Es suficiente para que el proyecto se sincronice con el IDE, se compile y pueda lanzarse a través de los comandos Gradle estándar.

En este punto la estrategia más correcta es no «mejorar» la estructura a mano. No hace falta mover los archivos del Wrapper «porque estorban». No hace falta renombrar paquetes en diez lugares «porque se me ocurrió». No hace falta crear una carpeta my_code junto a src «porque así es más fácil». El valor del esqueleto está precisamente en que es estándar: la estandarización es tu ahorro de tiempo futuro.

9. Errores típicos al analizar el skeleton del proyecto

Error n.º 1: percibir el proyecto generado como «un montón de basura que puede limpiarse».
A menudo la mano se extiende a borrar gradlew, la carpeta gradle/ y la mitad de los archivos en la raíz porque «no parecen código». Eso rompe la reproducibilidad del build y convierte el proyecto en «funciona solo en mi portátil». Si no estás seguro de para qué sirve el archivo, mejor primero entender su rol, y no borrarlo.

Error n.º 2: confundir src/main/java y src/main/resources.
Este error parece gracioso exactamente hasta el momento en que gastas una hora en la pregunta «¿por qué Spring no ve mi clase?», que está en resources. El código Java debe vivir en src/main/java, los recursos en src/main/resources. Eso no es un capricho, sino la convención sobre la que se apoya Gradle y el típico ecosistema Java.

Error n.º 3: cambiar caóticamente el paquete base y el nombre de la clase principal.
El paquete de la clase principal no es solo «belleza en el nombre», sino también un punto estructural importante del proyecto. Si renombras com.example.catalogservice en com.qwerty.myapp2.final, luego te sorprenderá que todo lo demás también hay que renombrarlo: carpetas, imports, tests. En el proyecto de aprendizaje es mejor mantener nombres estables y legibles y no hacer rebranding cada media hora.

Error n.º 4: ignorar el esqueleto de tests porque «no estoy aquí para tests».
El test contextLoads() parece vacío y quieres borrarlo como inútil. Pero sirve como la primera señal: el proyecto arranca como aplicación, el contexto se ensambla. Aunque de momento no escribas tests, tener una verificación mínima es como tener un botiquín de primeros auxilios en el coche: mejor que esté y no haga falta.

Error n.º 5: pensar que el starter es «una sola biblioteca» e intentar reemplazarlo por un conjunto manual de módulos.
A veces los principiantes ven «starter» y deciden: «Oh, soy listo, ahora voy a reemplazarlo por tres-cuatro módulos, para tenerlo todo bajo control». Normalmente eso acaba con que algo no se añadió, las versiones se desincronizaron y el proyecto deja de arrancar. El starter es una entrada conveniente. En el marco del curso nos mantenemos en el enfoque de starter para que el proyecto sea simple y predecible.

1
Tarea
Spring Boot, nivel 3, lección 3
Bloqueada
Archivos en sus lugares en el esqueleto generado
Archivos en sus lugares en el esqueleto generado
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION