Tutorial de Maven

Sep 2020

Maven es la herramienta por excelencia para controlar la construcción y descargar dependencias de cualquier aplicación Java. Desde la compilación hasta la construcción del binario final pasando por los test unitarios. En las siguientes líneas te enseñamos lo que debes saber para convertirte en un profesional de Maven.

Qué es Maven

Maven es una herramienta Open Source desarrollada en Java por la asociación Apache Foundation, un organismo sin ánimo de lucro que publica recursos libres de derechos para asegurar el desarrollo y la innovación en las tecnologías de la información. El objetivo de esta herramienta es facilitar y estandarizar la organización y la construcción de proyectos de software, principalmente para proyectos destinados a la máquina virtual de Java.

Cómo instalar Maven

Puedes consultar el artículo de instalación de maven para obtener un proceso detallado de cómo proceder para dejarlo bien configurado en tu estación de desarrollo.

Organización de archivos

Maven define que todo proyecto Java, ya sea para construir una librería o para una aplicación, debe tener la siguiente estructura de directorios en el sistema de ficheros:

  • pom.xml: fichero de descripción, ubicado en la raíz del proyecto. En él se indica su nombre, la ubicación que tendrá éste en el repositorio de binarios, las dependencias que necesita para compilar, para ejecutar o para lanzar los test. También se especifica si se hará uso de plugins.
  • src/main/java: directorio en donde se ubicarán los ficheros Java del proyecto que serán compilados y formarán parte del binario resultante. A partir de ese directorio es donde se tendrán en cuenta los paquetes de los ficheros Java. Por ejemplo, el fichero fuente de la clase org.proyecto.Persona se ubicará en src/main/java/org/proyecto/Persona.java.
  • src/main/resources: permite añadir recursos que también se incorporarán dentro del binario resultante, pero que no deben ser compilados. Por ejemplo, los ficheros con extensión .properties, XML o imágenes.
  • src/test/java: directorio para colocar las fuentes Java que únicamente se usarán en la ejecución de los test unitarios. El contenido de este directorio no será empaquetado en el binario final.
  • src/test/resources: necesario si para la ejecución de los test se utilizan otros recursos como ficheros XML o CSV con datos de prueba para verificar los algoritmos. Los ficheros incluidos no serán empaquetados en el fichero final.
  • target/classes: en este directorio se almacenarán las clases Java resultantes de la compilación de las fuentes almacenadas en src/main/java.
  • target/<proyecto.jar>: típicamente los proyectos generarán un binario con el contenido de target/classes más src/main/resources en un único archivo empaquetado con extensión jar. Si el proyecto es una aplicación, éste contendrá además todas las dependencias dentro de él para que se disponga de todo lo necesario a la hora de ejecutar la aplicación, formando un fichero denominado fat-jar.

Prácticamente todos los editores de programación de Java trabajan perfectamente con los proyectos organizados como indica Maven. Se puede considerar que se ha convertido en un estándar de facto. Los editores automáticamente procesan el fichero pom.xml para tener presente las dependencias necesarias y ajustar la configuración del proyecto acorde con lo especificado en el fichero.

Fases de construcción

Una vez definido cómo deben organizarse los ficheros fuente que constituyen un proyecto, el siguiente paso es invocar a Maven para que pueda realizar el proceso de construcción del proyecto. Maven tiene presente las dependencias entre módulos que puede haber, así que si se trata de un proyecto modular, el orden de construcción de los módulos se realizará en el orden correcto que permita satisfacer las dependencias. La construcción de cada módulo individual se realiza de la misma manera que se hace sobre un proyecto simple. Por tanto, ya sea para un módulo o para un proyecto simple, Maven tratará de construir el módulo o proyecto, pasando siempre por las siguientes fases en el orden que se indica:

  1. validate: valida que el proyecto es correcto y que toda la información necesaria está disponible según la especificación de los POMs.
  2. compile: compila el código fuente del proyecto revisando todos los directorios src/main/java, alojando los ficheros .class en target/classes.
  3. test: prueba el código fuente compilado utilizando el marco de prueba unitario indicado en el POM, normalmente JUnit. En esta fase se lanza la ejecución de todos los test. Si el resultado de alguno de ellos no es correcto se interrumpe la construcción.
  4. package: recoge el código compilado y lo empaqueta en su formato distribuible, normalmente como un fichero del tipo <application>.jar.
  5. verify: ejecuta cualquier comprobación de los resultados de las pruebas de integración para garantizar que se cumplan los criterios de calidad.
  6. install: instala el paquete en el repositorio local, para usarlo como dependencia en otros proyectos locales.
  7. deploy: copia el binario construido en el repositorio remoto, para compartirlo con otros desarrolladores y proyectos. Esta fase requiere que se indique la URL del repositorio donde desplegar, así como también las credenciales a usar en caso necesario.
  8. clean: (opcional) Si se invoca manualmente, Maven eliminará todos los recursos generados durante la compilación y el empaquetado, borrando el directorio target.
  9. site: (opcional) produce un informe en HTML acerca del proyecto mostrando la documentación del API de Javadoc y estadísticas según las herramientas de análisis que se hayan configurado.

En la mayoría de las veces será suficiente con lanzar la ejecución de Maven parando en algún momento anterior al deploy si se trabaja localmente. Por ejemplo, en muchas ocasiones bastará con ejecutar package si es una aplicación simple o install si se trata de la actualización de algún módulo usado por otra aplicación. Para indicar hasta qué punto se desea ejecutar la construcción de un proyecto, en la línea de comandos ubicándose el directorio donde está alojado el pom.xml, se invocará a Maven mediante el comando mvn argumentando la fase donde finalizar:

    # update target/classes
    mvn compile

    # install binary into local repository
    mvn install

    # both steps
    mvn compile install

    # clean resources before install
    mvn clean install

Sea cual sea la fase indicada, si pasa por package, Maven dejará disponible el binario localmente ubicado en el directorio target del proyecto o módulo correspondiente.

Gestión de dependencias

Uno de los mayores beneficios del uso de Maven es la buena gestión de dependencias que realiza la herramienta juntamente con el gran catálogo de librerías que ofrece públicamente. Todas ellas sin ningún coste de uso. Maven viene configurado de serie con unos repositorios públicos donde se despliegan la mayoría de librerías Open Source del ecosistema de Java. Para hacer uso de cualquiera, bastará con indicar una nueva dependencia en el POM del proyecto.

Cuando el proyecto requiere de una dependencia, Maven irá a buscarlo al repositorio local. Si la librería no está alojada ahí, normalmente un fichero con extensión .jar, tratará de descargarlo de Internet buscando en los repositorios públicos. Si lo encuentra remotamente, guardará una copia local para que esté disponible para la siguiente vez. El directorio por defecto del repositorio local suele estar en <home_usuario>/.m2/repository. Aunque puede indicarse una ruta alternativa en el fichero de configuración de Maven <home_usuario>/.m2/settings.xml. Mientras no se indique lo contrario, todas las dependencias de todos los proyectos serán almacenadas en ese directorio. Así que fácilmente pueden acumularse varios Gigas de información a medida que avanza el uso de diferentes librerías y versiones en el conjunto de los proyectos. El usuario es libre de eliminar el contenido de éste, provocando que Maven deba descargar de nuevo las dependencias necesarias.

Las dependencias del proyecto deben ir dentro de la sección de <dependencies>. Debe tenerse en cuenta que éstas pueden tener diferentes ámbitos de uso denominados scopes. El ámbito se indica en el atributo <scope> del XML y permite separar aquellas dependencias que son necesarias para compilar, de otras que serán sólo requeridas durante la ejecución del aplicativo. Maven soporta los siguientes scopes:

  • compile: es el ámbito por defecto en caso de que no se indique ningún otro. Se refiere a las dependencias ordinarias que son necesarias tanto para la compilación como para la ejecución. Por tanto deberán ir acompañando al binario de la aplicación final.
  • provided: son las dependencias que son necesarias para compilación y ejecución al igual que en el ámbito compile, pero con la diferencia de que no hace falta que se añadan al binario de la aplicación. Se considera que estas serán proporcionadas a la aplicación de forma externa a Maven. Este ámbito es útil cuando se despliega una aplicación en un servidor de aplicaciones el cual ya dispone de las librerías necesarias.
  • runtime: hace referencia a aquella que no es necesaria para la compilación, pero sí debe ir acompañando al binario de la aplicación final puesto que la ejecución del programa requerirá alguna funcionalidad de la librería citada.
  • test: reservado para aquellas otras que únicamente son requeridas para realizar las pruebas de test unitarios, por lo que no son necesarias ni para la compilación ni para la ejecución. Por tanto, no acompañarán al binario final.
  • system: a efectos prácticos es similar a compile, con la salvedad de que el fichero JAR es proporcionado manualmente por el programador, indicando la ruta del fichero. No será necesario que Maven lo resuelva como dependencia usando el repositorio local.
  • import: sirve para importar todas las dependencias definidas en otro artefacto de tipo POM. Requiere que las dependencias estén citadas en la sección del XML denominada <dependencyManagement> de este segundo POM.

En la mayoría de las ocasiones sólo será necesario indicar el ámbito de tipo test, puesto que el otro ámbito compile será aplicado por Maven automáticamente en ausencia del dato. Independientemente del ámbito, todas las dependencias deben ir dentro de la sección <dependencies>:

<project ...>
    ...
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        ...
    </dependencies>
    ...
</project>

La gran variedad de dependencias existentes en Maven hace difícil que el programador pueda recordar qué datos indicar a la hora de incluir una librería. Además, Java está compuesta por una comunidad muy activa de programadores que liberan nuevos recursos y actualizaciones con frecuencia. Todo ello complica la tarea de añadir una librería o artefacto dentro del proyecto. Para facilitar esta tarea existe una página web que ofrece una interfaz de usuario destinada al programador. En ella el usuario puede localizar y copiar la dependencia cómodamente en el POM del proyecto. Esta página hace uso de los mismos repositorios públicos que usa Maven indexando todo su contenido:

La página web ofrece estadísticas de uso de las principales librerías que pueden ser útiles para descubrir otros proyectos a tener presente en futuros desarrollos. A medida que los proyectos crecen, la lista de dependencias también lo hace y por regla de tres, las dependencias transitivas de las librerías incluidas. En ocasiones puede ocurrir que dos dependencias entren en conflicto al usar internamente versiones diferentes de una misma librería. Encontrar y resolver esta situación requiere un trabajo de investigación y de búsquedas por la red no trivial. Una opción que ayuda al diagnóstico es mediante Maven. Se le puede solicitar que muestre información de cómo ha resuelto las dependencias directas e indirectas, indicando qué versión de cada una de ellas se está aplicando.

Para ver el resultado de la resolución de los requerimientos debe ejecutarse el siguiente comando:

mvn dependency:tree

Esta sentencia imprimirá por consola la resolución de dependencias usadas durante la compilación y el empaquetado de la aplicación. Resulta muy útil a la hora de solucionar alguna incompatibilidad entre librerías usadas o comportamientos anómalos en tiempo de ejecución.

Primera aplicación con Maven

La manera más sencilla de realizar una primera aplicación con Maven y Java es invocar al asistente de Maven para que cree un esqueleto de aplicación. Para ello necesitamos hacerle llegar el grupo, y el nombre del project (artifactId) al asistente que puede invocarse desde el terminal.

Por ejemplo si queremos crear un proyecto con el grupo: org.eadp y artifact:my-app ejecutaremos el siguiente comando:

mvn archetype:generate \\
    -DarchetypeArtifactId=maven-archetype-quickstart \\
    -DinteractiveMode=false \\
    -DgroupId=org.eadp \\
    -DartifactId=my-app

La ejecución de un arquetipo de Maven no es más que la aplicación de una plantilla predeterminada que genera un esqueleto de aplicación, creando el contenido dentro del directorio con el mismo nombre que el dado al proyecto a través de la variable artifactId.

El proyecto puede ser importado sin ningún problema desde cualquier editor de programación de Java con soporte para Maven. Pruebe de realizar la construcción del proyecto con mvn package para ver qué sucede.

El generador crea un test inicial como plantilla para que el programador tenga una referencia rápida de cómo crear los tests y poder así aplicar una metodología de desarrollo basada en tests. Puede lanzar la ejecución de los tests desde el terminal de comandos con mvn test

En cualquier caso, le recomendamos que se familiarice con el uso de su IDE de Java preferido que simplificará enormemente las tareas de construcción, depuración, tests y cualquier otra tarea diaria del programador.

Déjanos tus comentarios abajo o escríbenos para compartir tu experiencia o para mejorar este contenido. Saludos!

Tutorial de Maven

¿Con ganas de seguir leyendo?

Nuestra guía de Java

Cerca de 450 páginas en un libro de tapa blanda que podrás utilizar para aprender a programar en Java desde cero sin conocimientos previos. Explicamos como usar las herramientas más usadas en el mundo empresarial, todas ellas son totalmente gratis y Open Source.

Aprende conceptos como TDD para desarrollar software con garantías. Conecta tus apps con JPA en bases de datos SQL. Integra tus proyectos con Maven y mantenlos bajo control con Git. Mantente al día con la programación funcional de Java 8+.

Nuestra guía de Java
Libro Javañol