2026-11-02
Avant Maven, construire un projet Java signifiait écrire soi-même un script de compilation, maintenir à la main la liste des bibliothèques du classpath et rejouer l’opération sur chaque machine. Le résultat est fragile : il dépend de la mémoire du scripteur et de l’état de son poste de travail.
Maven change de contrat. Au lieu de décrire comment construire, on décrit ce qu’est le projet : ses coordonnées, ses dépendances, sa structure. Maven en déduit une procédure de construction standard. Cette leçon pose le vocabulaire et la logique sur lesquels le TP s’appuie ; elle n’énumère pas de commandes.
À l’issue de cette leçon, vous devriez être capable de :
<parent>) et agrégation (<modules>) ;BUILD FAILURE et isoler la cause utile.Le fichier pom.xml, appelé POM (Project Object Model), est la seule configuration obligatoire d’un projet Maven. Il répond à trois questions : qui êtes-vous, de quoi avez-vous besoin, comment devez-vous être construit.
Le triplet groupId / artifactId / version forme les coordonnées du projet : il l’identifie de façon unique aux yeux de Maven, comme une adresse à trois niveaux. Une quatrième brique, packaging (jar, war, pom), précise la forme de l’artefact produit. Pour un projet Java standard, <packaging>jar</packaging> est la forme attendue ; jar étant la valeur par défaut, l’élément peut être omis sans changer le résultat — on le rend explicite quand il faut signaler une autre nature d’artefact.
Les dépendances ne vivent pas dans le projet. Elles sont téléchargées puis rangées dans le dépôt local, par défaut ~/.m2/repository, dont l’arborescence reproduit exactement les coordonnées :
Trois conséquences pratiques :
Maven définit plusieurs cycles de vie indépendants (clean, default, site). Une phase est une étape nommée et ordonnée : invoquer une phase exécute implicitement toutes celles qui la précèdent.
| Phase | Rôle | Ce qui est produit |
|---|---|---|
validate |
vérifier la cohérence du POM | rien |
compile |
compiler src/main/java |
target/classes |
test |
exécuter les tests de src/test/java |
rapports de test |
package |
assembler l’artefact | target/*.jar |
verify |
contrôles complémentaires sur l’artefact | rien de publié |
install |
publier l’artefact dans le dépôt local | entrée dans ~/.m2 |
deploy |
publier l’artefact dans un dépôt distant | artefact partagé |
Piège classique
clean n’appartient pas au cycle default. Écrire mvn clean package ne place pas une phase avant les autres : cela invoque deux cycles distincts dans l’ordre donné sur la ligne de commande.
Une phase n’accomplit rien par elle-même : elle est reliée à des goals (objectifs) apportés par des plugins. Formule à retenir : le cycle ordonne, le plugin fournit, le goal exécute.
Certains goals, comme dependency:tree, sont des outils de diagnostic invocables hors cycle. La configuration d’un plugin, elle, se déclare dans le POM et non sur la ligne de commande, sans quoi elle n’existerait que pour la frappe du jour :
Le scope d’une dépendance borne les étapes où elle apparaît dans le classpath, et décide de sa propagation transitive. Pour un jar Maven standard, l’artefact final n’embarque pas les dépendances ; l’emballage dans une archive exécutable dépend d’un autre mécanisme (war, shade, assembly).
| Scope | Compilation | Tests | Exécution | Transitif |
|---|---|---|---|---|
compile (défaut) |
oui | oui | oui | oui |
provided |
oui | oui | non | non |
runtime |
non | oui | oui | oui |
test |
non | oui | non | non |
provided : l’environnement d’exécution fournit la bibliothèque ; elle sert à compiler, elle ne doit pas être fournie à l’exécution ni empaquetée dans un artefact d’exécution sous peine de doublon.runtime : inutile pour compiler, indispensable pour exécuter — un pilote JDBC que le code ne nomme jamais en est l’exemple type.test : strictement confinée aux tests.Deux scopes supplémentaires existent (system, déprécié ; import, réservé au <dependencyManagement>) et ne sont pas mobilisés ici.
En déclarant une dépendance, vous héritez aussi de ses propres dépendances : ce sont les dépendances transitives. Le scope se propage : une dépendance compile expose ses dépendances compile et runtime en compile ; une dépendance test d’un tiers reste confinée à ce tiers.
Bénéfice : ne déclarer que ce dont le code se sert directement. Contrepartie : des versions que vous n’avez pas choisies peuvent être sélectionnées à votre place. Cette lecture est le premier réflexe lorsqu’un NoSuchMethodError apparaît à l’exécution alors que la compilation passait.
| Critère | Héritage | Agrégation |
|---|---|---|
| Déclaration | <parent> dans l’enfant |
<modules> dans le père |
| Effet | reprend propriétés, versions et configurations | construit plusieurs projets d’un seul appel (le reactor) |
| Implique une arborescence ? | non | oui : chaque module est un chemin déclaré |
Les deux relations se logent souvent dans le même fichier, d’où la confusion courante. Règle de conduite : le parent porte la politique (versions, encodage, options de compilation), chaque module garde la description de ce qu’il est. Un POM purement agrégateur se déclare en général <packaging>pom</packaging>.
mvn dépend d’une installation locale : deux machines, deux versions, deux comportements possibles. Le wrapper — mvnw, mvnw.cmd et le répertoire .mvn/wrapper/ — est un lanceur versionné qui télécharge et exécute la version de Maven inscrite dans le dépôt, et non celle laissée sur le poste.
Dans un projet versionné qui fournit un wrapper, les consignes doivent mentionner ./mvnw plutôt que mvn. Dans un TP où le projet est créé à la main et où le wrapper n’existe pas encore, les commandes peuvent rester écrites avec mvn ; générer le wrapper ensuite est une amélioration facultative. Le wrapper fige l’outil ; il ne dispense en rien de comprendre ce que la commande fait.
Un échec Maven est bavard, mais structuré :
Quatre questions, dans cet ordre :
Failed to execute goal …) et localise l’étape en cause. Si aucune goal n’est citée, l’échec est antérieur à l’exécution d’une tâche : il relève en général de la résolution — dépendances ou plugins introuvables — et c’est cette piste qu’il faut suivre d’emblée.on project …, lorsqu’un projet est identifié.[ERROR] porte la cause ; les suivantes sont souvent des conséquences. On ne lit qu’elle.Erreur fréquente
Could not resolve dependencies ne signifie pas « cette bibliothèque n’existe pas », mais « Maven ne l’a pas trouvée aux coordonnées indiquées, dans les dépôts qu’il connaît ». Relire la déclaration dans le POM est plus efficace que de vider le dépôt local, qui ne fait que masquer le problème et le faire rejouer. Pour trancher sans lancer une construction complète, dependency:resolve vérifie que chaque dépendance déclarée est bien atteignable ; dependency:tree en affiche le sous-arbre.
Sept idées à garder
groupId : artifactId : version — et l’arborescence du dépôt local en est la conséquence mécanique.clean est un autre cycle.<parent> est une relation d’héritage, <modules> une relation d’agrégation.[ERROR] est celui qui compte.Répondez en une ou deux phrases avant d’ouvrir le TP.
fr.example:billing:1.0.0 dans le dépôt local ?mvn test compile-t-il les sources principales ? Justifiez.src/test/java : quel scope ?test contamine-t-elle l’artefact final ?<modules> et chaque enfant déclare <parent> : qui relève de l’agrégation, qui relève de l’héritage ?mvn clean package échoue sur Could not resolve dependencies : citez deux causes distinctes à tester dans l’ordre.~/.m2/repository/fr/example/billing/1.0.0/billing-1.0.0.jar, avec son .pom.test est postérieure à compile, qui est donc exécutée par la même invocation.test.runtime.test n’est ni transitive, ni embarquée dans un jar standard.<modules> agrège, <parent> hérite.-U.Le TP 01_java_P_maven_lab.qmd met ces notions à l’épreuve sur un projet que vous manipulez vous-même : ses consignes fixent le détail des commandes, la présente leçon en fournit la logique. Revenez y chercher une définition au premier message d’erreur obscur — la cause est le plus souvent une coordonnée, un scope ou une phase mal interprétée.
E. Bruno - Maven : les concepts clés