# Día 1: Instalación de JDK, Dominio de Apache Maven y Anatomía de Dependencias

> Gestión de JDK 17 y 21, Maven Wrapper (mvnw), Anatomía del pom.xml, Scopes y Plugins

- Plan: [JavaMastery](https://aprende-java.webs-economicas.es/), día 1 de 30
- Fase 0: Setup Profesional, JDK y Dominio de Maven
- Dedicación: 3h
- Profundidad: para dominar
- Revisado: 2026-10-29
- Versión interactiva: https://aprende-java.webs-economicas.es/dias/jdk-maven-dependencias/

## Al terminar este día sabrás

- Tener JDK 17 y JDK 21 instalados y saber cambiar entre ellos sin romper Maven.
- Leer un `pom.xml` de Spring Boot y explicar qué hace cada bloque.
- Predecir qué ejecuta Maven al lanzar `mvn verify` y por qué.
- Elegir el scope correcto de una dependencia y diagnosticar conflictos de versiones con `dependency:tree`.


## Conceptos

### 1. Gestión de Múltiples JDKs (17 y 21): JAVA_HOME y SDKMAN!

Una máquina de desarrollo a menudo necesita alternar entre Java 17 LTS (para proyectos corporativos vigentes) y Java 21 LTS (para nuevos desarrollos con Virtual Threads). SDKMAN! en Linux/macOS/WSL o gestores de variables de entorno en Windows gestionan el valor de la variable `JAVA_HOME` y el `PATH`.

**Cómo funciona por dentro.**

Cuando escribes `java` o `javac` en la terminal, el sistema operativo busca el primer ejecutable con ese nombre en el `PATH`. Maven, en cambio, no mira el `PATH`: usa la variable `JAVA_HOME` para localizar el compilador. Por eso es posible que `java -version` diga 21 y Maven compile con 17. El primer diagnóstico siempre es `mvn -v`, que imprime la versión de Maven y, debajo, la ruta y versión del JDK que realmente está usando.

Cada versión del JDK genera bytecode con un número de versión de clase (61 para Java 17, 65 para Java 21). Una JVM solo ejecuta clases de su versión o anteriores. Si compilas con 21 y despliegas en un servidor con Java 17, el arranque falla con `UnsupportedClassVersionError`. La opción `<release>` del compilador evita el problema: además de fijar la versión del bytecode, restringe las APIs del JDK a las que existían en esa versión.

SDKMAN! (Linux, macOS, WSL) instala varios JDK en paralelo y cambia `JAVA_HOME` por ti. Con un archivo `.sdkmanrc` en la raíz del proyecto, `sdk env` activa automáticamente la versión correcta al entrar en la carpeta. En Windows nativo el equivalente es fijar `JAVA_HOME` por terminal o usar un gestor como Scoop.

*Instalar dos JDK y fijar uno por proyecto*

```bash
# Linux / macOS / WSL con SDKMAN!
sdk list java | grep tem          # distribuciones Eclipse Temurin disponibles
sdk install java 21.0.4-tem
sdk install java 17.0.12-tem
sdk default java 21.0.4-tem       # versión global

# Fijar la versión dentro de un proyecto concreto
echo "java=17.0.12-tem" > .sdkmanrc
sdk env                           # activa la versión del .sdkmanrc

# Comprobar QUÉ JDK usa Maven (no basta con java -version)
mvn -v
# Apache Maven 3.9.9
# Java version: 17.0.12, vendor: Eclipse Adoptium, runtime: /home/ana/.sdkman/candidates/java/17.0.12-tem

# Windows (PowerShell), solo para la sesión actual
# $env:JAVA_HOME = "C:\Program Files\Eclipse Adoptium\jdk-21.0.4.7-hotspot"
# $env:Path = "$env:JAVA_HOME\bin;$env:Path"
```

- **En una línea:** `sdk install java 21.0.2-tem && sdk use java 17.0.10-tem`
- **Por qué importa:** Si `JAVA_HOME` no apunta al JDK correcto, Maven compilará con una versión inesperada, provocando errores de versión de bytecode (UnsupportedClassVersionError).
- **Error típico:** Instalar solo el JRE (Java Runtime Environment) en vez del JDK (Java Development Kit), lo que impide tener `javac` para compilar.
- **Java 17 frente a 21:** En Java 17 el bytecode generado tiene versión de clase 61; en Java 21 tiene versión de clase 65.
- **Se conecta con:** Compilación de Bytecode, Variables de Entorno, Eclipse Temurin / Corretto

**Compruébalo:** `java -version` muestra 21, pero `mvn -v` muestra 17. ¿Qué está pasando?

El `PATH` apunta a un JDK 21, pero `JAVA_HOME` sigue apuntando a un JDK 17. Maven solo mira `JAVA_HOME`. Corrige la variable (o usa `sdk use`) y vuelve a comprobar con `mvn -v`.

### 2. Apache Maven y el Maven Wrapper (mvnw): La Garantía de Reproducibilidad

Maven es la herramienta de gestión de dependencias y construcción estándar en Java. El Maven Wrapper (`mvnw` en Unix, `mvnw.cmd` en Windows) es un script autoejecutable que descarga automáticamente la versión exacta de Maven definida en `.mvn/wrapper/maven-wrapper.properties` sin obligar al desarrollador a instalar Maven globalmente.

**Cómo funciona por dentro.**

El Maven Wrapper es un pequeño script (`mvnw` para Unix y `mvnw.cmd` para Windows) más un archivo `.mvn/wrapper/maven-wrapper.properties` que indica la versión exacta de Maven a usar. La primera vez que lo ejecutas descarga esa versión en `~/.m2/wrapper` y, a partir de ahí, la reutiliza. Nadie del equipo necesita instalar Maven, y el servidor de integración continua construye con la misma versión que tu portátil.

Esto importa porque el comportamiento de Maven cambia entre versiones: versiones por defecto de plugins, resolución de dependencias, soporte de nuevas versiones de Java. Un proyecto que compila con Maven 3.9 puede fallar con un Maven 3.6 instalado hace años en otra máquina.

Los tres archivos del wrapper se suben al repositorio. Spring Initializr ya los genera; en un proyecto existente se añaden con `mvn wrapper:wrapper`. Recuerda dar permiso de ejecución a `mvnw` en Linux (`chmod +x mvnw`) y no confundir el repositorio local de dependencias (`~/.m2/repository`) con la descarga del propio Maven.

*Añadir y usar el wrapper*

```bash
# Generar el wrapper fijando la versión de Maven
mvn wrapper:wrapper -Dmaven=3.9.9

# Archivos generados (todos van a Git)
# mvnw
# mvnw.cmd
# .mvn/wrapper/maven-wrapper.properties

cat .mvn/wrapper/maven-wrapper.properties
# distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip

# A partir de ahora, en vez de "mvn":
./mvnw clean verify          # Linux / macOS
mvnw.cmd clean verify        # Windows

chmod +x mvnw                # si Git perdió el permiso de ejecución
```

- **En una línea:** `./mvnw wrapper:wrapper -Dmaven=3.9.6`
- **Por qué importa:** Garantiza que cualquier miembro del equipo o el pipeline de CI/CD construya el proyecto con exactamente la misma versión de Maven, eliminando el clásico "en mi máquina sí compila".
- **Error típico:** Ejecutar `mvn clean package` global con una versión antigua de Maven (e.g. 3.6) que no soporta Java 21 en lugar de usar siempre `./mvnw clean package`.
- **Java 17 frente a 21:** Idéntico para Java 17 y 21.
- **Se conecta con:** CI/CD, Docker, Gestión de Proyectos

### 3. Anatomía del pom.xml: Coordenadas GAV y Convención sobre Configuración

El `pom.xml` (Project Object Model) se rige por las coordenadas GAV: `groupId` (e.g. `com.miempresa`), `artifactId` (nombre del artefacto, e.g. `order-service`) y `version` (e.g. `1.0.0-SNAPSHOT`). Maven sigue la convención sobre configuración: el código fuente DEBE residir en `src/main/java`, los recursos en `src/main/resources`, los tests en `src/test/java`, y el compilado se genera en `target/`.

**Cómo funciona por dentro.**

Las coordenadas GAV (`groupId`, `artifactId`, `version`) identifican de forma única cualquier artefacto en un repositorio Maven. `groupId` suele ser el dominio invertido de la organización, `artifactId` el nombre del módulo y `version` sigue versionado semántico; el sufijo `-SNAPSHOT` indica una versión en desarrollo que puede cambiar.

En un proyecto Spring Boot, el bloque `<parent>` apunta a `spring-boot-starter-parent`. Ese padre aporta dos cosas: un `dependencyManagement` con las versiones probadas de cientos de librerías (por eso tus dependencias de Spring no llevan `<version>`) y una configuración por defecto de plugins (compilador, Surefire, el plugin de Spring Boot). Cambiar la versión de Java es tan simple como la propiedad `java.version`.

Convención sobre configuración significa que Maven asume una estructura de carpetas: `src/main/java` para el código, `src/main/resources` para la configuración, `src/test/java` para tests y `target/` para lo generado. Si respetas la convención, el `pom.xml` queda corto y cualquier desarrollador Java sabe orientarse en tu proyecto.

*pom.xml mínimo de Spring Boot comentado*

```xml
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>

  <!-- Hereda versiones de dependencias y configuración de plugins -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.4</version>
  </parent>

  <!-- Coordenadas GAV de TU proyecto -->
  <groupId>com.acme</groupId>
  <artifactId>taskflow</artifactId>
  <version>0.1.0-SNAPSHOT</version>

  <properties>
    <!-- El parent lo traduce a <release>21</release> en el compilador -->
    <java.version>21</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <!-- sin <version>: la fija el dependencyManagement del parent -->
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- Empaqueta un JAR ejecutable con java -jar -->
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>
```

- **En una línea:** `<groupId>com.estudio</groupId><artifactId>java-mastery</artifactId><version>0.0.1-SNAPSHOT</version>`
- **Por qué importa:** No reinventar la rueda: cualquier desarrollador Java del mundo que abra tu proyecto sabe al instante dónde encontrar cada archivo.
- **Error típico:** Colocar archivos `.properties` o `.sql` dentro de `src/main/java`, lo que provoca que Maven los ignore al compilar y no lleguen al JAR final.
- **Java 17 frente a 21:** Aplica a cualquier versión de Java.
- **Se conecta con:** Estructura de Directorios, Empaquetado JAR/WAR, Repositorio Maven Local (.m2)

### 4. El Ciclo de Vida de Maven (Build Lifecycle) y sus Fases

Maven opera mediante un ciclo de vida con fases secuenciales ordenadas: `validate` (verifica el pom), `compile` (compila código a `.class`), `test` (ejecuta tests unitarios), `package` (empaqueta el JAR en `target/`), `verify` (ejecuta tests de integración), `install` (copia el JAR a tu repositorio local `~/.m2/repository`), y `deploy` (publica en Nexus/Artifactory remoto).

**Cómo funciona por dentro.**

Maven tiene tres ciclos de vida independientes: `clean` (borra `target/`), `default` (construye) y `site` (documentación). El ciclo `default` es una secuencia ordenada de fases; las más usadas son `validate`, `compile`, `test`, `package`, `verify`, `install` y `deploy`. Pedir una fase ejecuta todas las anteriores en orden.

Las fases, por sí solas, no hacen nada. El trabajo lo hacen los goals de los plugins, que están enlazados a fases. Por ejemplo, `maven-compiler-plugin:compile` se enlaza a `compile`, `maven-surefire-plugin:test` a `test`, `maven-jar-plugin:jar` a `package` y `maven-failsafe-plugin` a `integration-test` y `verify`. Cuando añades un plugin al `pom.xml` y lo enlazas a una fase, pasa a ejecutarse cada vez que se alcance esa fase.

En el día a día: `./mvnw verify` es el comando de integración continua (compila, pasa tests unitarios, empaqueta y pasa tests de integración). `install` copia el artefacto a tu `~/.m2/repository` para que otros proyectos locales lo usen; no lo necesitas en un proyecto de un solo módulo.

*Qué ejecuta cada comando*

```bash
./mvnw compile        # validate -> compile
./mvnw test           # ... -> compile -> test-compile -> test (Surefire: *Test.java)
./mvnw package        # ... -> test -> package (genera target/taskflow-0.1.0-SNAPSHOT.jar)
./mvnw verify         # ... -> package -> integration-test -> verify (Failsafe: *IT.java)
./mvnw clean verify   # primero el ciclo clean, después el default hasta verify

# Ejecutar un goal concreto sin pasar por el ciclo de vida
./mvnw dependency:tree
./mvnw spring-boot:run

# Ver qué goals están enlazados a cada fase en TU proyecto
./mvnw help:describe -Dcmd=verify

# Saltar tests (con cuidado): compila los tests pero no los ejecuta
./mvnw package -DskipTests
```

- **En una línea:** `./mvnw clean package`
- **Por qué importa:** Al ejecutar una fase tardía (e.g. `mvn package`), Maven ejecuta automáticamente todas las fases previas (`validate`, `compile`, `test`).
- **Error típico:** Hacer `mvn install` constantemente en proyectos grandes cuando solo se deseaba probar si compilaba (`mvn compile` o `mvn test`), saturando el `.m2` local con artefactos intermedios.
- **Java 17 frente a 21:** Universal en el ecosistema Java.
- **Se conecta con:** Plugins de Maven, Surefire vs Failsafe, Automatización

### 5. Scopes de Dependencias: Para qué son y cómo se utilizan

El elemento `<scope>` define en qué etapas del ciclo de vida está disponible una librería: `compile` (por defecto: en compilación, test y runtime del JAR final), `test` (solo al compilar y ejecutar tests, nunca entra al JAR, e.g. `junit-jupiter`, `assertj`), `provided` (necesaria para compilar pero proporcionada por el entorno en ejecución, e.g. `lombok`), y `runtime` (no se necesita para compilar pero sí para ejecutar, e.g. driver de base de datos como `postgresql` o `h2`).

**Cómo funciona por dentro.**

El scope decide en qué classpath aparece una dependencia: el de compilación del código principal, el de los tests o el de ejecución, y si se propaga a quien dependa de tu proyecto.

- `compile` (por defecto): disponible al compilar, en tests y en ejecución. Se propaga transitivamente.
- `provided`: disponible al compilar, pero no se empaqueta porque el entorno ya la aporta (por ejemplo, la API de Servlet en un servidor de aplicaciones o Lombok).
- `runtime`: no puedes importarla en tu código, pero se empaqueta y está presente al ejecutar. Caso típico: el driver JDBC, porque tu código solo habla con la interfaz `java.sql`.
- `test`: solo en `src/test/java`; nunca llega al JAR final.
- `import`: solo dentro de `dependencyManagement`, para importar un BOM con versiones.

Usar el scope correcto reduce el tamaño del artefacto, evita que el código de producción dependa por accidente de librerías de test y obliga a programar contra interfaces en lugar de implementaciones concretas.

*Los scopes en un proyecto real*

```xml
<dependencies>
  <!-- compile: tu código usa sus clases directamente -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

  <!-- runtime: solo se habla con java.sql.*, el driver se carga al arrancar -->
  <dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
  </dependency>

  <!-- provided + optional: procesador de anotaciones que no debe ir al JAR -->
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>provided</scope>
    <optional>true</optional>
  </dependency>

  <!-- test: JUnit 5, AssertJ y Mockito llegan con este starter -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>
```

- **En una línea:** `<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><scope>test</scope></dependency>`
- **Por qué importa:** Evita inflar el tamaño del JAR con librerías innecesarias de testing en producción y previene que el código de producción importe clases de prueba accidentalmente.
- **Error típico:** Dejar una dependencia de testing como `h2` o `mockito` con scope `compile` por omitir el `<scope>test</scope>`, exponiendo clases de prueba en producción.
- **Java 17 frente a 21:** Idéntico en 17 y 21.
- **Se conecta con:** Resolución Transitiva, Dependency Management, Optimización de Contenedores Docker

**Compruébalo:** Si importas `org.postgresql.Driver` en una clase de `src/main/java` con el driver en scope `runtime`, ¿qué ocurre?

La compilación falla: con scope `runtime` la clase no está en el classpath de compilación. Es justo lo que se busca, porque tu código no debería acoplarse al driver concreto.

### 6. Gradle frente a Maven

Gradle es la otra gran herramienta de build en Java. Describe el proyecto con un script en Kotlin (`build.gradle.kts`) o Groovy en lugar de XML, ejecuta tareas en vez de fases y reutiliza resultados con builds incrementales y una caché de compilación.

**Cómo funciona por dentro.**

Gradle modela el build como un grafo de tareas (`compileJava`, `test`, `bootJar`) en lugar de un ciclo de vida fijo. El proyecto se describe en `build.gradle.kts` con el DSL de Kotlin (o en Groovy, `build.gradle`), así que puede contener lógica, y `settings.gradle.kts` declara el nombre y los submódulos. Igual que Maven, tiene un wrapper (`gradlew`) que fija la versión de Gradle del proyecto.

Su punto fuerte es el rendimiento: solo vuelve a ejecutar las tareas cuyas entradas cambiaron, mantiene un proceso en segundo plano (daemon) y puede reutilizar resultados de una caché local o remota. En proyectos grandes con muchos módulos la diferencia con Maven se nota. A cambio, al ser código, un build de Gradle puede volverse difícil de entender si cada equipo añade su propia lógica.

Equivalencias para leer un proyecto: `implementation` es el scope `compile`, `runtimeOnly` es `runtime`, `testImplementation` es `test` y `compileOnly` se parece a `provided`. `./gradlew build` compila, ejecuta los tests y empaqueta, como `mvn package`, y `./gradlew dependencies` muestra el árbol de dependencias.

*El mismo proyecto Spring Boot con Gradle (Kotlin DSL)*

```kotlin
// build.gradle.kts
plugins {
    java
    id("org.springframework.boot") version "3.5.0"
    id("io.spring.dependency-management") version "1.1.7"
}

java {
    toolchain { languageVersion = JavaLanguageVersion.of(21) }   // como maven.compiler.release
}

repositories { mavenCentral() }

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")       // scope compile
    runtimeOnly("org.postgresql:postgresql")                                  // scope runtime
    testImplementation("org.springframework.boot:spring-boot-starter-test")  // scope test
}

tasks.test { useJUnitPlatform() }

// ./gradlew build          compila, prueba y genera build/libs/*.jar
// ./gradlew dependencies   árbol de dependencias (como mvn dependency:tree)
```

- **En una línea:** `dependencies { implementation("org.springframework.boot:spring-boot-starter-web"); testImplementation("org.springframework.boot:spring-boot-starter-test") }`
- **Por qué importa:** Muchas empresas y casi todos los proyectos Android usan Gradle. Spring Initializr te deja elegir entre los dos, así que conviene saber leer ambos.
- **Error típico:** Mezclar `mvn` y `gradle` en el mismo proyecto o subir la carpeta `build/` al repositorio. Usa siempre el wrapper (`./gradlew`), igual que con Maven.
- **Java 17 frente a 21:** Java 17 funciona con Gradle 7.3 o superior; para ejecutar el build con Java 21 hace falta Gradle 8.5 o superior.
- **Se conecta con:** Gradle Wrapper (gradlew), Spring Initializr, Builds incrementales


## Sesión de 90 minutos

### Bloque 1: Configuración de JDK y Maven Wrapper (0 - 25 min)

- **Qué estudiar:** Verificación de `java -version`, `javac -version`, estructura del script `mvnw` y archivo `.mvn/wrapper/maven-wrapper.properties`.
- **Pregunta a responder:** ¿Cómo asegurar que cualquier persona del equipo use Java 17 o 21 sin configurar Maven manual?
- **Entregable:** Proyecto base generado con `./mvnw --version` reportando compatibilidad con el JDK instalado.

### Bloque 2: Anatomía del pom.xml y Fases del Ciclo de Vida (25 - 50 min)

- **Qué estudiar:** Estructura de `src/main/java`, `src/test/java`, etiquetas `groupId`, `artifactId`, `version`, y ejecución de `mvn clean compile`, `mvn test`, `mvn package`.
- **Pregunta a responder:** ¿Qué archivos se generan en la carpeta `target/` y cómo limpiarlos?
- **Entregable:** Compilación limpia con `./mvnw clean package` generando el artefacto `.jar` en `target/`.

### Bloque 3: Scopes y Diagnóstico con dependency:tree (50 - 80 min)

- **Qué estudiar:** Diferencias entre scopes `compile`, `test`, `runtime`, `provided`. Uso de `./mvnw dependency:tree` para auditar librerías transitivas.
- **Pregunta a responder:** ¿Qué librería introduce dependencias no deseadas o vulnerables en el proyecto?
- **Entregable:** Salida de `dependency:tree` analizada con dependencias de test correctamente aisladas.

### Cierre y Consolidación (80 - 90 min)

- **Qué estudiar:** Repaso de las 5 reglas de oro de Maven y configuración del plugin de compilador con `<release>17</release>` o `<release>21</release>`.
- **Pregunta a responder:** ¿Por qué `<release>` es superior a `<source>` y `<target>` antiguos en Java moderno?
- **Entregable:** Pom.xml optimizado sin dependencias con versiones quemadas a mano.


## Explícalo desde cero

### Por qué Maven utiliza un repositorio local (~/.m2) y cómo resuelve dependencias

- **Qué entender:** Cuando declaras una dependencia, Maven no la descarga una y otra vez para cada proyecto. La busca primero en tu caché local (`~/.m2/repository`). Si no está, la descarga desde Maven Central (o el Nexus corporativo) y la almacena allí. Si 10 proyectos usan Spring Boot 3.2.0, el binario solo se almacena una vez en el disco de tu máquina.
- **Qué suele explicarse mal:** Creer que `pom.xml` "instala" cosas como si fuera un gestor de paquetes de sistema operativo (apt o brew). En realidad solo define un grafo de dependencias para el classpath del proyecto.
- **Ejemplo sencillo:** Como una biblioteca municipal: si ya tienes el libro prestado en tu estantería local (~/.m2), lo lees de inmediato; si no, vas a la biblioteca central a pedir una copia.
- **Señal de alerta:** Si borras el directorio de tu proyecto pensando que eso elimina las dependencias descargadas de tu ordenador, o si no sabes dónde inspeccionar el archivo `.m2` cuando hay un jar corrupto.

### Diferencia entre <dependencies> y <dependencyManagement> (BOMs)

- **Qué entender:** `<dependencies>` añade librerías reales al classpath. `<dependencyManagement>` NO añade nada; solo define las versiones recomendadas y compatibles de las dependencias. Spring Boot utiliza una BOM (Bill of Materials) dentro de `dependencyManagement` para que tú puedas declarar `spring-boot-starter-web` sin especificar `<version>`, garantizando que todas las librerías de Spring sean 100% compatibles entre sí.
- **Qué suele explicarse mal:** Poner números de versión manuales a cada dependencia de Spring Boot, anulando la resolución automática del starter y provocando colisiones de versiones (jar hell).
- **Ejemplo sencillo:** `<dependencyManagement>` es el menú de precios del restaurante; `<dependencies>` es el plato que realmente pides a la mesa.
- **Señal de alerta:** Ver un `pom.xml` de Spring Boot lleno de etiquetas `<version>3.2.1</version>` en dependencias estándar oficiales.


## Preguntas

### Nivel 1 (Básico): ¿Qué diferencia práctica hay entre una dependencia con `<scope>test</scope>` y una con `<scope>runtime</scope>`?

Evalúa si entiendes el Classpath de Java. `test` permite importar las clases en `src/test/java` pero las excluye del empaquetado final. `runtime` impide importar las clases directamente en el código Java durante la compilación (evita acoplamiento a una implementación concreta), pero incluye el JAR en la ejecución final (típico del driver JDBC de PostgreSQL o MySQL).

**Respuesta razonada:** Las dos dependencias se excluyen de algún sitio, pero de sitios distintos. Con `test`, la dependencia está disponible para compilar y ejecutar `src/test/java` y no se empaqueta. Con `runtime`, no está disponible para compilar `src/main/java` (no puedes escribir `import` de sus clases), pero sí se empaqueta y está en el classpath al arrancar la aplicación.

Ejemplo: JUnit va en `test` porque producción nunca lo necesita. El driver de PostgreSQL va en `runtime` porque producción lo necesita para conectarse, pero tu código solo debe depender de las interfaces de `java.sql` o de JPA.

### Nivel 2 (Intermedio): Si dos librerías en tu `pom.xml` dependen de versiones distintas de `jackson-databind` (una de 2.15.0 y otra de 2.16.0), ¿cómo decide Maven cuál usar y cómo inspeccionas el conflicto?

Evalúa el conocimiento del algoritmo de resolución de dependencias de Maven: "Nearest definition wins" (la definición más cercana en el árbol de dependencias gana; si están al mismo nivel, gana la primera declarada). Se inspecciona ejecutando `./mvnw dependency:tree`.

**Respuesta razonada:** Maven aplica la regla de **mediación por cercanía**: gana la versión más cercana a tu proyecto en el árbol de dependencias. Si están a la misma profundidad, gana la que se declaró primero en el `pom.xml`. No elige la más moderna, lo que puede provocar `NoSuchMethodError` en ejecución si una librería necesitaba una API de la versión que perdió.

Para inspeccionarlo: `./mvnw dependency:tree -Dverbose -Dincludes=com.fasterxml.jackson.core` muestra qué versión se omitió y por qué (`omitted for conflict with`). Para resolverlo, fija la versión en `<dependencyManagement>` (o confía en el BOM de Spring Boot) y, en proyectos grandes, activa la regla `dependencyConvergence` del `maven-enforcer-plugin` para que el build falle ante conflictos.

### Nivel 3 (Entrevista Senior): ¿Para qué sirve el plugin `spring-boot-maven-plugin:repackage` y por qué un JAR normal generado por Maven no arranca directamente con `java -jar`?

Evalúa la arquitectura de los Fat JARs / Uber JARs. Un JAR estándar de Maven solo contiene las clases compiladas de tu proyecto (`src/main/java`), sin incluir los JARs de dependencias externas. El plugin de Spring Boot reempaqueta el JAR creando una estructura especial (`BOOT-INF/classes`, `BOOT-INF/lib`) e inyectando un cargador de clases (`org.springframework.boot.loader.JarLauncher`) capaz de leer JARs anidados dentro de un JAR.

**Respuesta razonada:** Un JAR normal de Maven solo contiene tus clases compiladas: ni las dependencias ni un `Main-Class` en el `MANIFEST.MF`. Al hacer `java -jar` la JVM no sabe qué clase arrancar y, aunque lo supiera, faltarían Spring, Tomcat, Jackson, etc.

El goal `repackage` del `spring-boot-maven-plugin` se ejecuta después de `package` y reescribe el JAR: mete tus clases en `BOOT-INF/classes`, las dependencias en `BOOT-INF/lib` y pone como `Main-Class` el `JarLauncher` de Spring Boot, que crea un classloader capaz de leer JARs anidados y luego invoca tu clase `@SpringBootApplication` (indicada como `Start-Class`). El JAR original se conserva como `*.jar.original`.


## Reto del tutor

**Reto:** Un desarrollador junior añade una dependencia en su `pom.xml` para conectar a Oracle Database, pero escribe `<scope>provided</scope>` porque "el servidor de la empresa ya tiene instalada la base de datos Oracle". ¿Qué ocurrirá cuando compile y cuando ejecute la aplicación con `java -jar` localmente? Corrige su razonamiento como un líder técnico.

- **Lo que está bien:** Identifica que `provided` significa que el entorno de ejecución (JVM) debe proporcionar el JAR, y que una base de datos externa no es lo mismo que el driver JDBC en la JVM.
- **Respuesta superficial:** Decir que "dará error de compilación".
- **Lo que falta de fondo:** No comprender que el código compilará sin ningún error (si solo usa interfaces estándar de `java.sql`), pero al arrancar en local fallará inmediatamente con `ClassNotFoundException: oracle.jdbc.OracleDriver` porque el driver no fue empaquetado en el JAR.
- **Respuesta senior:** Confundió el motor de base de datos con el conector de software (driver JDBC). El servidor Oracle puede estar corriendo en la red, pero la JVM de la aplicación necesita el archivo JAR del driver (`ojdbc11`) en su classpath para poder comunicarse por TCP/IP. Al usar `provided`, Maven no mete el driver dentro del archivo JAR ejecutable, esperando que un servidor de aplicaciones antiguo (como Tomcat o WebLogic) lo inyecte en su carpeta `/lib`. En una aplicación moderna autocontenida de Spring Boot 3, el driver debe tener scope `runtime` para que viaje empaquetado dentro de `BOOT-INF/lib/`.


## Katas

### Kata 1.1: Creación de un Proyecto Maven con Maven Wrapper y pom.xml Estructurado

Genera un proyecto Maven limpio con coordenadas `com.estudio:java-mastery:1.0.0-SNAPSHOT`, configura el compilador con `<release>21</release>`, añade `junit-jupiter` con scope `test`, y ejecuta `./mvnw clean test` comprobando que compila y pasa las fases con éxito.

### Kata 1.2: Auditoría y Detección de Conflictos con dependency:tree

Añade una dependencia a tu `pom.xml`, ejecuta `./mvnw dependency:tree -Dverbose` y analiza en la consola qué librerías secundarias (transitivas) fueron descargadas automáticamente en tu directorio `~/.m2/repository`.


## Reglas de oro

- Usa SIEMPRE el Maven Wrapper (`./mvnw`) en tus repositorios; jamás dependas del `mvn` que el desarrollador tenga instalado globalmente.
- Las librerías de prueba (JUnit, Mockito, AssertJ, Testcontainers) deben tener SIEMPRE `<scope>test</scope>`.
- Los drivers JDBC (PostgreSQL, H2, MySQL) se declaran con `<scope>runtime</scope>` para no acoplar el código de compilación a la implementación del vendor.
- Jamás pongas números de versión fijos en librerías gestionadas por el starter de Spring Boot (`spring-boot-starter-parent`); deja que la BOM resuelva la versión compatible.
- Ejecuta periódicamente `./mvnw dependency:tree` para detectar conflictos de versiones transitivas (JAR Hell) antes de subir a producción.


## Java 17 frente a Java 21

| Característica | Java 17 | Java 21 |
| --- | --- | --- |
| Configuración Compilador | <release>17</release> | <release>21</release> |
| Versión Bytecode (.class) | Clase versión 61 | Clase versión 65 |
| Herramienta de Construcción Recomendada | Maven 3.8.6+ o Gradle 7.3+ | Maven 3.9.5+ o Gradle 8.5+ |
| Soporte Spring Boot 3.x | Línea base mínima requerida | Soporte nativo completo para Virtual Threads |


## Proyecto guiado «Pedidos»: Crear el proyecto

Tener un proyecto Maven que cualquiera pueda clonar y compilar con el mismo comando.

**Tareas:**

- Genera el proyecto `pedidos` con el Maven Wrapper y `maven.compiler.release` en 21.
- Crea los paquetes `com.tienda.pedidos.dominio` y `com.tienda.pedidos.aplicacion`.
- Añade JUnit 5 y AssertJ con scope `test` y un test vacío que pase.
- Sube el proyecto a un repositorio Git con un `.gitignore` de Maven.

**Está listo cuando:**

- `./mvnw verify` termina en verde en un ordenador sin Maven instalado.
- `./mvnw dependency:tree` no muestra dependencias de test en el scope `compile`.


## Fuentes oficiales

- [Maven: guía de inicio](https://maven.apache.org/guides/getting-started/)
- [Maven: ciclo de vida del build](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html)
- [Maven: mecanismo de dependencias](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html)
- [Dev.java: primeros pasos con Java](https://dev.java/learn/getting-started/)

