Si llevas poco tiempo programando en Java, probablemente te has preguntado cómo los proyectos profesionales organizan sus librerías externas, compilan el código, ejecutan los tests y generan el archivo final listo para desplegar. La respuesta, en la mayoría de los casos, es Maven.
Maven es la herramienta de construcción y gestión de dependencias más usada en el ecosistema Java. Entiéndelo como el equivalente a npm en JavaScript o pip en Python, pero con mucho más poder: no solo descarga librerías, sino que define todo el ciclo de vida de tu proyecto, desde la compilación hasta el despliegue.
¿Qué problema resuelve Maven?
Antes de Maven, gestionar un proyecto Java era tedioso. Si querías usar una librería externa, tenías que buscarla manualmente, descargar el archivo .jar, copiarlo en una carpeta del proyecto y configurar el classpath para que el compilador lo encontrara. Si esa librería dependía a su vez de otras librerías, tenías que rastrear y descargar todas ellas también. Y si un compañero de equipo clonaba el repositorio, tenía que repetir todo el proceso.
Maven resuelve esto de raíz con tres ideas fundamentales:
- Convención sobre configuración: Maven define una estructura de proyecto estándar. Si la sigues, casi nada necesita configuración explícita.
- Gestión declarativa de dependencias: declaras qué librerías necesitas en un archivo XML y Maven las descarga automáticamente, junto con todas sus dependencias transitivas.
- Ciclo de vida estándar: compilar, testear, empaquetar y desplegar son fases predefinidas que cualquier proyecto Maven entiende.
Instalación de Maven
Maven requiere Java instalado en el sistema. Puedes verificar si ya tienes ambos con:
java -version
mvn -versionSi Maven no está instalado, la forma más sencilla de instalarlo según tu sistema operativo es:
# macOS con Homebrew
brew install maven
# Ubuntu / Debian
sudo apt update && sudo apt install maven
# Windows con Chocolatey
choco install maven
# O manualmente: descarga desde https://maven.apache.org/download.cgi,
# descomprime y añade el directorio bin/ al PATH del sistema.Tras la instalación, mvn -version debería mostrar algo como:
Apache Maven 3.9.x
Maven home: /usr/local/Cellar/maven/3.9.x/libexec
Java version: 21, vendor: Eclipse Adoptium
La estructura estándar de un proyecto Maven
La convención de Maven define dónde va cada cosa. Si respetas esta estructura, no necesitas configurar nada adicional:
mi-proyecto/
├── pom.xml ← archivo de configuración principal
├── src/
│ ├── main/
│ │ ├── java/ ← código fuente de la aplicación
│ │ │ └── com/ejemplo/
│ │ │ └── App.java
│ │ └── resources/ ← archivos de configuración, plantillas
│ │ └── application.properties
│ └── test/
│ ├── java/ ← código de los tests
│ │ └── com/ejemplo/
│ │ └── AppTest.java
│ └── resources/ ← recursos usados solo en tests
└── target/ ← generado por Maven (no subir a git)
├── classes/ ← bytecode compilado
└── mi-proyecto-1.0.jar ← artefacto final empaquetado
La carpeta target/ la genera Maven automáticamente. Añádela siempre al .gitignore de tu proyecto.
El archivo pom.xml: el corazón de Maven
POM significa Project Object Model. Es el archivo XML donde describes todo lo referente a tu proyecto: su identidad, sus dependencias, los plugins que usa y cualquier configuración adicional. Maven lee este archivo para saber qué tiene que hacer.
Un pom.xml mínimo tiene esta estructura:
<?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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- Identidad del proyecto -->
<groupId>com.ejemplo</groupId>
<artifactId>mi-proyecto</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Mi Proyecto</name>
<description>Un proyecto de ejemplo con Maven</description>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>Las tres coordenadas que identifican de forma única cualquier artefacto en el ecosistema Maven son:
groupId: identifica la organización o grupo al que pertenece el proyecto. Por convención se usa el dominio inverso de la empresa o desarrollador:com.google,org.apache,com.tuempresa.artifactId: el nombre del proyecto o módulo concreto. Debe ser único dentro del groupId.version: la versión del artefacto. El sufijo-SNAPSHOTindica que es una versión en desarrollo, no una versión estable publicada.
Gestión de dependencias
Añadir una dependencia a tu proyecto es tan simple como declarar sus coordenadas dentro del bloque <dependencies> del pom.xml. Maven la busca en su repositorio central, la descarga junto con todas sus dependencias transitivas y la deja disponible en tu proyecto.
<dependencies>
<!-- Jackson: para serializar y deserializar JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
<!-- Apache Commons Lang: utilidades para String, arrays, etc. -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
<!-- SLF4J + Logback: sistema de logging -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.3</version>
</dependency>
<!-- JUnit 5: framework de tests (solo en scope test) -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
¿Cómo encuentras las coordenadas de una dependencia? El repositorio central de Maven está en search.maven.org o mvnrepository.com. Busca el nombre de la librería y encontrarás el bloque XML listo para copiar.
Ámbitos de dependencia (dependency scope)
El atributo <scope> controla en qué fases del ciclo de vida está disponible la dependencia y si se incluye en el artefacto final. Es importante usarlo correctamente para no inflar el tamaño del JAR con librerías que solo se necesitan en tiempo de compilación o en los tests.
| Scope | Compilación | Tests | Ejecución | JAR final | Uso típico |
|---|---|---|---|---|---|
compile | Sí | Sí | Sí | Sí | Valor por defecto. Librerías de uso general. |
test | No | Sí | No | No | JUnit, Mockito, AssertJ. |
provided | Sí | Sí | No | No | La provee el servidor (Servlet API en Tomcat). |
runtime | No | Sí | Sí | Sí | Drivers JDBC: no se usan en compilación. |
import | - | - | - | No | Importar BOMs de gestión de versiones. |
Gestión centralizada de versiones con dependencyManagement
En proyectos con múltiples módulos o con muchas dependencias, es fácil tener versiones inconsistentes de la misma librería en diferentes lugares. La sección <dependencyManagement> permite definir las versiones en un único lugar y referenciarlas desde cualquier módulo sin repetirlas.
<properties>
<jackson.version>2.17.0</jackson.version>
<junit.version>5.10.2</junit.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- En los módulos hijos, se declara sin version: -->
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<!-- La version se hereda de dependencyManagement -->
</dependency>
</dependencies>
Importar un BOM (Bill of Materials)
Un BOM es un pom.xml especial que define versiones compatibles entre sí de un conjunto de librerías relacionadas. Spring Boot, por ejemplo, publica un BOM que garantiza que todas sus dependencias son compatibles entre sí. Importarlo es la forma más segura de evitar conflictos de versiones.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Ahora puedes añadir dependencias de Spring sin especificar version -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
El ciclo de vida de Maven
Maven define tres ciclos de vida estándar. El más importante es el default, que cubre el proceso completo de construcción de un proyecto. Cada fase ejecuta automáticamente todas las fases anteriores.
# Compilar el código fuente
mvn compile
# Ejecutar los tests unitarios
mvn test
# Empaquetar en un JAR (ejecuta compile + test + package)
mvn package
# Instalar el artefacto en el repositorio local (~/.m2)
mvn install
# Limpiar la carpeta target/
mvn clean
# Lo más habitual: limpiar y empaquetar desde cero
mvn clean package
# Limpiar y empaquetar saltándose los tests (útil para deploys rápidos)
mvn clean package -DskipTests
El repositorio local de Maven está en ~/.m2/repository. La primera vez que ejecutas un proyecto, Maven descarga todas las dependencias de Internet y las guarda ahí. En las ejecuciones siguientes las toma del caché local, por lo que es mucho más rápido.
Plugins: extendiendo las capacidades de Maven
Los plugins son los que realmente ejecutan el trabajo en Maven. Hay plugins para compilar, testear, generar documentación, analizar código, crear imágenes Docker y prácticamente cualquier tarea que puedas imaginar.
Se configuran en la sección <build><plugins> del pom.xml:
<build>
<plugins>
<!-- Plugin del compilador: especificar versión de Java -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>21</source>
<target>21</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- Surefire: ejecutar tests con JUnit 5 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
<!-- Shade: crear un JAR ejecutable con todas las dependencias incluidas -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.2</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.ejemplo.App</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Proyectos multi-módulo
Cuando tu aplicación crece, tiene sentido dividirla en módulos independientes: uno para el dominio, otro para la capa de datos, otro para la API REST. Maven soporta esto de forma nativa con el concepto de proyecto multi-módulo.
mi-aplicacion/ ← módulo padre (solo tiene pom.xml)
├── pom.xml
├── dominio/ ← lógica de negocio
│ ├── pom.xml
│ └── src/
├── repositorio/ ← acceso a base de datos
│ ├── pom.xml
│ └── src/
└── api/ ← endpoints REST
├── pom.xml
└── src/
<!-- pom.xml del módulo padre -->
<project>
<groupId>com.ejemplo</groupId>
<artifactId>mi-aplicacion</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging> <!-- debe ser "pom", no "jar" -->
<modules>
<module>dominio</module>
<module>repositorio</module>
<module>api</module>
</modules>
</project>
<!-- pom.xml de un módulo hijo -->
<project>
<parent>
<groupId>com.ejemplo</groupId>
<artifactId>mi-aplicacion</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>dominio</artifactId>
<dependencies>
<!-- dependencias específicas de este módulo -->
</dependencies>
</project>
Ejecutar mvn clean install en el módulo padre compila, testea y empaqueta todos los módulos en el orden correcto, respetando sus dependencias entre sí.
Perfiles: configuración por entorno
Los perfiles de Maven permiten tener configuraciones diferentes según el entorno en el que se construye el proyecto: desarrollo, testing, producción.
<profiles>
<profile>
<id>desarrollo</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<db.url>jdbc:h2:mem:testdb</db.url>
<log.nivel>DEBUG</log.nivel>
</properties>
</profile>
<profile>
<id>produccion</id>
<properties>
<db.url>jdbc:postgresql://prod-server:5432/miapp</db.url>
<log.nivel>WARN</log.nivel>
</properties>
<build>
<plugins>
<!-- Plugin adicional de optimización solo en producción -->
</plugins>
</build>
</profile>
</profiles>
# Activar un perfil específico al construir
mvn clean package -Pproduccion
# Verificar qué perfiles están activos
mvn help:active-profilesComandos esenciales del día a día
# Crear un nuevo proyecto Maven desde cero
mvn archetype:generate \
-DgroupId=com.ejemplo \
-DartifactId=mi-proyecto \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.4 \
-DinteractiveMode=false
# Ver el árbol completo de dependencias (muy útil para detectar conflictos)
mvn dependency:tree
# Ver solo las dependencias con conflictos de versión
mvn dependency:tree -Dverbose | grep -i conflict
# Analizar qué dependencias se usan y cuáles están de más
mvn dependency:analyze
# Descargar las fuentes y el Javadoc de todas las dependencias
mvn dependency:sources
mvn dependency:resolve -Dclassifier=javadoc
# Actualizar las versiones de las dependencias a las más recientes
mvn versions:display-dependency-updates
mvn versions:use-latest-releases
# Generar el site con la documentación del proyecto
mvn site
# Ver información detallada del pom efectivo (con toda la herencia resuelta)
mvn help:effective-pom
Maven Wrapper: garantizar la misma versión en todo el equipo
Maven Wrapper es una herramienta que incluye un script en el propio proyecto para descargar y usar una versión específica de Maven, sin depender de la que cada desarrollador tenga instalada. Es la práctica recomendada en equipos y en integración continua.
# Añadir el wrapper al proyecto
mvn wrapper:wrapper
# Esto genera:
# .mvn/wrapper/maven-wrapper.properties ← define la versión de Maven
# mvnw (Linux/Mac)
# mvnw.cmd (Windows)
# A partir de ahora, usar ./mvnw en lugar de mvn
./mvnw clean package
./mvnw test
./mvnw spring-boot:run
El archivo maven-wrapper.properties especifica la URL exacta de descarga de Maven. Todos los desarrolladores del equipo y el servidor de CI usarán exactamente la misma versión, eliminando el clásico problema de "en mi máquina funciona".
Maven vs Gradle
La alternativa más popular a Maven en el mundo Java es Gradle. Ambas herramientas resuelven el mismo problema pero con filosofías diferentes:
| Característica | Maven | Gradle |
|---|---|---|
| Configuración | XML (pom.xml) | Groovy o Kotlin DSL |
| Curva de aprendizaje | Menor | Mayor |
| Velocidad de build | Estándar | Más rápido (builds incrementales y caché) |
| Flexibilidad | Convención estricta | Muy flexible y scriptable |
| Ecosistema Java/Spring | Muy maduro | Muy maduro |
| Android | No usado | Estándar de facto |
| Proyectos empresariales | Predominante | Creciente adopción |
Para proyectos empresariales con Java y Spring Boot, Maven sigue siendo la opción dominante y la más segura si el equipo no tiene experiencia previa con Gradle. Para proyectos Android, Gradle es el único estándar. En proyectos nuevos donde la velocidad de build importa o el equipo ya conoce Gradle, este puede ser la mejor elección.
Resumen
- Maven gestiona el ciclo de vida completo de un proyecto Java: compilación, tests, empaquetado y despliegue.
- El archivo
pom.xmldefine la identidad del proyecto, sus dependencias, plugins y configuración. - Las dependencias se identifican con tres coordenadas:
groupId,artifactIdyversion. Maven las descarga automáticamente de Maven Central junto con sus dependencias transitivas. - El atributo
scopecontrola cuándo está disponible una dependencia y si se incluye en el artefacto final. Usa siemprescope=testpara JUnit y herramientas de testing. - Usa
dependencyManagementpara centralizar las versiones en proyectos con múltiples módulos e importa BOMs para garantizar la compatibilidad entre librerías relacionadas. - Los comandos más usados son
mvn clean packagepara construir ymvn dependency:treepara diagnosticar conflictos. - Incluye Maven Wrapper (
mvnw) en tu proyecto para garantizar que todo el equipo use la misma versión de Maven.
No hay comentarios todavía. Sé el primero en compartir tu opinión.