Saltar al contenido principal

Maven y ciclo de build

Ya tenemos una estructura reconocible, pero compilar cada .java a mano exige recordar rutas, orden y opciones. Si otra persona clona el proyecto, necesita un procedimiento conocido que produzca el mismo resultado y falle de forma visible cuando algo no funciona.

Maven resuelve ese problema describiendo el proyecto en pom.xml y ofreciendo un ciclo de build común. Maven no sustituye a Java: coordina herramientas y tareas para validar, compilar, probar y empaquetar el proyecto.

Un POM mínimo para Java 17​

POM significa Project Object Model. Este archivo real y ejecutable identifica el proyecto, fija Java 17 y evita depender de la codificación de la máquina:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>es.skilly</groupId>
<artifactId>gestor-tareas</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>

<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
</plugins>
</build>
</project>

Las coordenadas distinguen este artefacto de los demás:

  • groupId: organización o espacio propietario, aquí es.skilly;
  • artifactId: nombre técnico del proyecto, gestor-tareas;
  • version: versión concreta que estamos construyendo;
  • packaging: tipo de artefacto, en este caso un JAR.

maven.compiler.release pide al compilador clases y API compatibles con Java 17. Es preferible a combinar source y target, porque también limita la API disponible. La versión del plugin queda fijada para que el build no cambie por seleccionar otro plugin implícitamente.

Lifecycles, fases y goals​

Un lifecycle es una secuencia ordenada. El lifecycle principal, llamado default, contiene fases como:

  1. validate: comprueba que el proyecto puede procesarse;
  2. compile: compila producción;
  3. test: ejecuta los tests;
  4. package: crea el artefacto, por ejemplo el JAR;
  5. verify: ejecuta las comprobaciones previstas hasta esa fase.

Al pedir una fase, Maven ejecuta también las anteriores necesarias del mismo lifecycle. Por eso:

mvn package

no se limita a crear un JAR: antes valida, compila y ejecuta los tests.

clean pertenece a un lifecycle separado. Elimina la salida anterior del proyecto; no forma parte del lifecycle default:

mvn clean

Podemos encadenar ambos lifecycles:

mvn clean verify

Primero elimina target/ y después recorre el lifecycle principal hasta verify.

Una fase expresa un punto del ciclo, como compile. Un plugin aporta tareas concretas y un goal es una de esas tareas. Maven enlaza goals de plugins con las fases según el tipo de proyecto. No necesitamos memorizar todos esos enlaces para usar el flujo habitual.

Recorrer el build​

Desde la raíz, donde está pom.xml, podemos observar cada paso:

mvn validate
mvn compile
mvn test
mvn package
mvn verify
mvn clean
mvn clean verify

Después de compilar aparece target/classes. Tras package obtenemos:

target/gestor-tareas-1.0.0.jar

El JAR contiene las clases de producción, pero este POM no declara una clase principal en su manifiesto. Por tanto, no debemos documentarlo como ejecutable mediante java -jar. Crear un artefacto y convertirlo en una aplicación ejecutable son decisiones distintas.

Leer el resultado​

Una ejecución correcta termina con:

[INFO] BUILD SUCCESS

Un fallo termina con BUILD FAILURE y un código de salida distinto de cero. Ese código permite que un script detecte el resultado sin interpretar visualmente toda la salida.

En Bash podemos consultarlo justo después:

mvn test
echo $?

El valor 0 significa éxito; otro valor indica fallo. No basta con que aparezcan muchas líneas verdes: debemos comprobar la fase solicitada y el resultado final.

Reproducir desde un checkout limpio​

Una comprobación útil evita depender de clases antiguas:

mvn clean verify

Para que sea reproducible, el repositorio debe incluir el código, pom.xml y los recursos necesarios, pero no target/. Tras clonar, otra persona debería poder ejecutar el mismo comando con Java 17 y Maven y obtener el mismo artefacto.

Práctica​

Crea src/main/java/es/skilly/tareas/app/Main.java:

package es.skilly.tareas.app;

public class Main {
public static void main(String[] args) {
System.out.println("Gestor preparado");
}
}

Usa el POM anterior y comprueba:

  1. mvn validate acepta el modelo;
  2. mvn compile crea target/classes;
  3. mvn test tiene éxito aunque todavía no haya tests;
  4. mvn package crea target/gestor-tareas-1.0.0.jar;
  5. mvn verify completa el lifecycle;
  6. mvn clean elimina target/;
  7. mvn clean verify reconstruye todo desde cero.

Solución razonada​

Si los archivos están en la ruta correcta, no hace falta enumerarlos: Maven conoce src/main/java. El nombre del JAR combina artifactId y version. Al ejecutar clean, desaparece porque es un resultado generado, y verify lo reconstruye.

Si Maven muestra BUILD FAILURE, busca el primer mensaje que explique la causa, corrígela y repite desde la fase apropiada. En la próxima unidad añadiremos una necesidad nueva al POM: obtener una biblioteca externa sin copiar JARs manualmente.