4. Maven#
Maven es un gestor de proyectos escritos en Java, esto es, una herramienta que automatiza la construcción y la gestión de aplicaciones escritas en Java, lo cual implica simplificar las tareas de creación, gestión de dependencias, compilación, pruebas, empaquetado y despliegue de la aplicación.
Para ello provee de una orden (mvn) provista de subórdenes que modularizan las distintas tareas de las que se encarga.
Nota
Visual Studio Code tiene una extensión que nos abstrae de todos estos comandos, por lo que este apéndice no tiene más interés qué tener una idea de qué es lo que subyace por debajo.
4.1. Creación#
Se impone una estructura estandarizada para el proyecto que podemos resumir en el siguiente gráfico:
+-- src
| +-- main/
| | +-- java/edu/acceso/miapp/ (Código de la aplicación)
| | +-- resources/ Archivos de configuración, etc.
| |
| +-- test/ Reproduce la estruct. de main para pruebas
|
+-- target/ Código generado
+-- pom.xml Configuración del proyecto.
El subcomando asociado a esta fase es archetype:generate
:
$ mvn archetype:generate -DgroupId=edu.acceso.test \
-DartifactId=miapp
-DarchetypeArtifactId=maven-archetype-quickstart \
-Dversion=1.0.0 \
-DinteractiveMode=false
Pero lo habitual es que el IDE nos abstraiga de esta fase y se encargue el mismo de ejecutar la orden cuando le pidamos iniciar el proyecto.
4.2. Dependencias#
También facilita el gestor la tarea de obtener las dependencias de nuestro
proyecto descargándolas directamente del Repositorio General de Maven. Basta
incluirla en pom.xml
. Por ejemplo:
<dependencies>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.2.0</version>
</dependency>
<!-- Otras dependencias -->
</dependencies>
La página tiene un buscador y puede buscarse a través de él la librería. La del ejemplo se haya en la dirección persistence-api, y escogiendo la versión deseada, se obtiene el código exacto que debe añadirse al archivo. Las librerías, además, pueden a su vez contener dependencias, pero se calculan si necesidad de incluirlas explícitamente.
La suborden asociada a la obtención de dependencias es:
$ mvn dependency:resolve
$ mvn dependency:tree # Muestra el árbol de dependencias.
Sin embargo, no es necesario efectuar esta operación explícitamente, porque se realiza automáticamente al realizar operaciones posteriores.
4.3. Compilación#
La compilación, esto es, la generación de bytecode se realiza a través del plugin maven-compiler-plugin. Hacer la operación, en principio, es sencillo:
$ mvn compile # Genera el bytecode.
$ mvn clean compile # Genera borrando antes el código generado previo
Ahora bien, hay distintas versiones de Java y, al respecto, el compilador (javac) necesita que se le indiquen dos cosas:
Para qué versión de Java se realiza la comprobación estática de código.
Para qué versión de Java se genera el bytecode[1].
En ausencia de configuración al respecto, el plugin le indica al compilador
que use para ambas cosas la antigua version 1.7[2], por lo que es muy
recomendable indicar explícitamente en el archivo pom.xml
una versión
algo más moderna, e indispensable si se van a usar características del lenguaje
más modernas. Puede hacerse como una propiedad:
<properties>
<maven.compiler.release>21</maven.compiler.release>
</properties>
o directamente configurando explíciamente el plugin (que en principio no necesita ser declarado):
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<release>21</release>
</configuration>
</plugin>
</plugins>
</build>
Consejo
Configurarlo como propiedad permite usar el valor declarado de la versión en otras partes del archivo, que necesiten indicar también la versión. Por ejemplo, al configurar el plugin para generar la documentación con Javadoc.
Nota
La opción --release
que engloba ambos aspectos de la compilación
apareció en Java 9. Antes no existía y se usaban en su lugar --source
y
--target
(que, no obstante, siguen existiendo), por lo que si se quiere
mantener compatibilidad con un JDK antiguo podemos hacer:
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
o la configuración correspondiente directamente en el plugin.
4.4. Pruebas#
El código que prueba la aplicación debe escribirse bajo src/test/
con
la misma estructura que se uso en src/main/
.
Las subórdenes asociadas a esta fases son:
$ mvn test # Compila todo el código (principal y pruebas) y ejecuta las pruebas
$ mvn test-compile # Sólo compila las pruebas sin llegar a ejecutarlas.
4.5. Empaquetado#
El software compilado suele distribuirse en formato JAR, un archivo zip que
contiene las clases compiladas (archivos .class
) y algunos otros
archivos adicionales. Para crearlo:
$ mvn clean package
Para la generación del paquete, Maven usa el plugin maven-jar-plugin, que no incluye dependencias. Para más información sobre ello y cómo incluirlas consulte este gist sobre generación de paquetes.
4.6. Arquetipos#
Al crear un nuevo proyecto, Maven define su estructura inicial tomando como referencia lo que se llama un arquetipo (o ninguno). Como es probable que para resolver las tareas del módulo partamos siempre de una misma base, nos ahorrará mucho tiempo crear un arquetipo personal, así que pongámonos a ellos.
Lo primero es crear un arquetipo en un directorio:
$ mvn archetype:generate \
-DgroupId=edu.acceso.arquetipo \
-DartifactId=acceso_datos
-DarchetypeArtifactId=maven-archetype-archetype \ # Arquetipo base
-DarchetypeVersion=1.5 \ # Versión del arquetipo base
-Dversion=1.0 \ # Versión de este nuevo arquetipo.
-DinteractiveMode=false
Esto definirá la estructura básica del arquetipo en el directorio
acceso_datos
:
+-- pom.xml (pom.xml del propio arquetipo)
|
+-- src/
+-- main/resources/
| +-- archetype-resources/
| +-- pom.xml (pom.xml del proyecto)
| +-- src/ ...
| +-- META-INF/maven/
| +-- archetype-metadata.xml
+-- pom.xml
+-- test/ ...
Como no nos interesan las pruebas podemos eliminar src/test/
y debemos
centrarmos en:
El
pom.xml
del propio arquetipo.El contenido de
src/main/resources/archetype-resources/
que contiene los archivos que contendrá nuestro proyecto.El archivo
src/main/resources/META-INF/maven/archetype-metadata.xml
que contiene la configuración para generar el nuevo proyecto.
En consecuencia, todos los archivos que queramos que acaben en nuestro proyecto deberán incluirse dentro de la segunda ubicación; y en el tercer archivo deberemos definir la configuración:
<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor name="acceso-datos"
xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0 http://maven.apache.org/xsd/archetype-descriptor-1.1.0.xsd">
<fileSets>
<fileSet filtered="true" packaged="false">
<directory></directory>
<includes>
<include>README.md</include>
<include>LICENSE</include>
</includes>
</fileSet>
<fileSet filtered="true" packaged="false">
<directory>.vscode</directory>
<includes>
<include>**/*.json</include>
</includes>
</fileSet>
<fileSet filtered="true" packaged="true">
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
</fileSets>
<requiredProperties>
<requiredProperty key="groupId" />
<requiredProperty key="artifactId" />
<requiredProperty key="version">
<defaultValue>1.0.0</defaultValue>
</requiredProperty>
<requiredProperty key="dollar">
<defaultValue>$</defaultValue>
</requiredProperty>
<requiredProperty key="hash">
<defaultValue>#</defaultValue>
</requiredProperty>
</requiredProperties>
</archetype-descriptor>
En la configuración tenemos dos partes fundamentales:
Por un lado declaramos los archivos que queremos que se incluyan en el proyecto. En la configuración hay tres bloques de archivos. La indicación filtered significa que queremos que el plugin analice el archivo en busca de propiedades para que las sustituya por su valor; y packaged que queremos que se añada la declaración de paquete:
package edu.acceso.paquete...;
al comienzo del archivo.Por otro, definimos cuáles son las propiedades y los valores por defecto que tienen. Por ejemplo, la variable artifactId podrá referirse en los archivos como
${artifactId}
y, en ese caso, se sustituirá por el valor asignado.
Advertencia
Hay archivos como .gitignore
que el plugin no incluirá en
el proyecto aunque lo creemos.
Pues bien, una vez creado el arquetipo deberíamos retocar su contenido para ajustarlo a nuestras necesidades.
Nota
Se incluye un arquetipo preparado
que puede descargar, modificar a su gusto e instalar.
Una vez que tengamos preparado el arquetipo, debemos acceder a su directorio e instalarlo:
$ cd acceso_datos/
$ mvn clean install
Truco
Con clean
nos aseguramos de que cualquier compilación anterior del
arquetipo no interfiera.
Instalado, podemos comprobar que realmente lo está y registrarlo para que el IDE nos permita seleccionarlo:
$ ls ~/.m2/repository/edu/acceso/arquetipo/acceso_datos/ # Si instalado, existirá.
$ mvn archetype:crawl # Lo registramos.
$ cat ~/.m2/repository/archetype-catalog.xml # Comprobamos el registro.
En caso de que quisiéramos eliminarlo podríamo eliminar a mano:
$ rm -rf ~/.m2/repository/edu/acceso/arquetipo/acceso_datos/
$ mvn archetype:crawl
En principio, podríamos probar a generar un nuevo proyecto sin llegar a usar el IDE:
$ mvn archetype:generate \
-DarchetypeGroupId=edu.acceso.arquetipo \
-DarchetypeArtifactId=acceso_datos \
-DarchetypeVersion=1.0 \ # Versión de arquetipo (en su pom.xml)
-DgroupId=edu.acceso.test_test \
-DartifactId=test_test \
-DinteractiveMode=false
Prudencia
Téngase presente que hay dos números de versión: la versión del
propio arquetipo que es la que se indica aquí y que se define en el archivo
pom.xml
del propio arquetipo (el que se encuentra en el directorio
raíz, acceso_datos
en nuestro ejemplo) y la versión de la aplicación
que pretendemos desarrollar, que se puede proporcionar mediante
-Dversion=
, pero que no hemos fijado y hemos preferido indicar en el
archivo archetype-metadata.xml a través de la propiedad correspondiente.
También podremos crear el proyecto a través del IDE siempre que nos hayamos asegurado de que el arquetipo se registró.
Notas al pie