Dependencias con maven

De ChuWiki


Uno de los puntos fuertes de maven son las dependencias. En nuestro proyecto podemos decirle a maven que necesitamos un jar (por ejemplo, log4j o el conector de MySQL) y maven es capaz de ir a internet, buscar esos jar y bajárselos automáticamente. Es más, si alguno de esos jar necesitara otros jar para funcionar, maven "tira del hilo" y va bajándose todos los jar que sean necesarios. Vamos a ver todo esto con un poco de detalle.

Repositorios maven[editar]

En internet hay repositorios de jars preparados para que los entienda maven y ahí es donde maven va a buscar los jar cuando los necesita. Las dependencias se irán bajando a nuestro ordenador para tenerlas disponibles y la almacena en un repositorio local.

maven central[editar]

El repositorio oficial de maven es el repositorio maven central http://repo1.maven.org/maven2/ Si navegamos por esa URL iremos encontrando un montón de jar de un montón de librerías de libre distribución. Por ejemplo, si nuestro proyecto necesita el conector de MySQL, maven encontrará las distintas versiones de estos jar en https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/

Pero no te preocupes, no es necesario navegar por este repositorio para buscar la dependencia que necesitamos. Hay una forma más sencilla que explicamos más adelante.

repositorio local[editar]

Cuando maven se baja una dependencia del repositorio central, la copia en un repositorio local en nuestro ordenador. Por defecto, está en la carpeta ${user.home}/.m2/repository. De esta forma, no tiene que mirar internet continuamente cada vez que compilemos un proyecto. Puedes cambiar esta ubicación si quieres en el fichero settings.xml de configuración de Maven.

Un detalle a tener en cuenta. Si la versión de una dependencia es un número "normal" de versión, por ejemplo, 1.4.23, maven considera que ese jar ya es estable y no va a sufrir cambios. Así que si lo tiene en el repositorio local, no volverá a buscarlo en el repositorio central. Sin embargo, si la versión termina con la palabra mágica -SNAPSHOT, por ejemplo, 1.1.0-SNAPSHOT, maven considera que ese jar está en desarrollo. Así que la primera vez que compiles cada día, ira a ver si hay actualizaciones en maven central para bajarlas a tu repositorio local. Con mvn -U ... puedes forzar a que busque y se baje actualizaciones en cualquier momento.

Añadir dependencias maven a nuestro proyecto[editar]

Para indicarle a maven que necesitamos un jar determinado, debemos editar el fichero pom.xml que tenemos en el directorio raíz de nuestro proyecto. En el pom.xml generado por defecto por maven veremos un trozo como el siguiente:

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

Vemos que hay un bloque entre etiquetas <dependencies>. Dentro hay un bloque <dependency> con una dependencia de junit.

Esta es una dependencia que se pone automáticamente cuando creamos nuestro proyecto con maven. Presupone que vamos a usar junit y en concreto, la versión 3.8.1. Si nosotros queremos añadir más dependencias, debemos poner más trozos como este. Siempre entre los tag <dependencies> y </dependencies>.

Buscar la dependencia en mvnrepository[editar]

Si queremos añadir una dependencia y tenemos una idea del nombre de la misma, la forma más cómoda es buscarla en mvnrepository. Por ejemplo, si queremos el conector con MySQL y en la caja de búsqueda escribimos mysql, obtendrás el siguiente resultado

Búsqueda de una dependencia maven en mvnrepository
Búsqueda de una dependencia maven en mvnrepository

Seleccionamos el que necesitemos en la lista que sale. Nos aparecerá un listado con las versiones del driver y seleccionamos, nuevamente, la que nos interese

Seleccionar la versión de la dependencia maven en mvnrepository
Seleccionar la versión de la dependencia maven en mvnrepository

Nos aparecerá una caja justo con el trozo <dependency> que necesitamos poner en nuestro fichero pom.xml. Nos bastaría copiarlo y añadirlo en nuestro pom.xml

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.2.0</version>
        <scope>runtime</scope>
    </dependency>
  </dependencies>

Hemos añadido a mano <scope>. Con esta dependencia en concreto, no venía ese parámetro en mvnrepository. Normalmente suele venir. En cualquier caso, conviene saber lo que estamos copiando para poder adaptarlo en caso necesario.

groupId, artifactId, version[editar]

Veamos qué significa cada una de las etiquetas que hemos puesto. Como comentamos en tutorial de maven, maven identifica los jar por tres parámetros: groupId, artifactId y version.

  • El groupId identifica normalmente a un proyecto o una empresa. Yo por ejemplo, suelo poner como groupId en todos mis proyectos com.chuidiang. MySQL ha decidido poner com.mysql.
  • El artifactId es el nombre concreto del jar.
  • version es el número de versión que queremos.

scope de las dependencias[editar]

El <scope> es para qué necesitamos el jar. Hay varias opciones, pero las más habituales son test, compile, runtime y provided

  • test, quiere decir que ese jar sólo se necesita para ejecutar los test o programas de prueba. El ejemplo evidente es el propio jar de junit.
  • compile quiere decir que es necesario para que nuestro jar compile. Es decir, nuestros fuentes java instancian o usan directamente clases de la librería, hace import de esas clases, etc.
  • runtime es que sólo se necesita en tiempo de ejecución, pero no para compilar. Habitualmente es el caso de los drivers de conexión con base de datos, ya que para conectarnos a una base de datos usamos las clases estándar del paquete java.sql, no las del driver. Sin embargo, necesitamos que en ejecución esté el driver presente.
  • provided. Similar a compile o runtime, pero con una diferencia importante. Con provided estamos indicando que esta dependencia ya estará instalada en el entorno donde vayamos a ejecutar. Por ejemplo, la dependencia de javax.servlet-api en una aplicación web suele venir en el servidor de aplicaciones (Tomcat, Wildfly, etc). Por ello, si hacemos una aplicación war con maven, no necesitamos que esta dependencia vaya dentro del war. Las dependencias provided no se empaquetan en el war o en el zip para distribuir.

Descarga de las dependencias[editar]

Una vez hecho esto, si compilamos nuestro proyecto con mvn compile o mvn package, veremos como maven se descarga de internet estos jars, siempre y cuando no los tengamos ya (sólo veremos la descarga la primera vez que ejecutemos el comando).

Si vas a tu directorio ${user.home}/.m2/repository y navegas, verás que las dependencias se han bajado ahí.

Dependencias transitivas[editar]

Como hemos comentado, si ponemos una dependencia en nuestro proyecto que a su vez tiene otras dependencias, no necesitamos ponerlas todas en nuestro pom.xml. Maven es inteligente y si una dependencia necesita otras, va tirando de ellas y las trae todas a nuestro repositorio local.

Esto es lo que se conoce como dependencias transitivas en maven. Dependemos indirectamente de dependencias de las dependencias. El comando "mvn dependency:tree" te muestra todas estas dependencias. Un ejemplo de salida de este comando podría ser este

[INFO] com.chuidiang.examples.myproject:myproyect:jar:1.0-SNAPSHOT
[INFO] +- junit:junit:jar:4.11:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.3:test

Vemos que la dependencia de junit necesita a su vez hamrest-core. Maven se bajará ambas a nuestro repositorio local.

Descargar las dependencias transitivas[editar]

No necesitas hacerlo para el uso normal de Maven. Maven las descarga y almacena en el repositorio local, por lo que las tiene disponibles para tu proyecto. No obstante, hay ocasiones que necesitas tenerlas, quizás porque no quieres trabajar con maven en tu IDE favorito o las necesitas para hacer un zip y poder distribuir tu programa. Y en tu repositorio local habrá muchas de tu proyecto y de otros proyectos y propias de maven y encima están en una estructura de subdirectorios relativamente compleja. El comando mvn dependency:copy-dependencies las baja y guarda en el directorio target/dependencies de tu proyecto.