Accès rapide :
Qu'est-ce qu'un module Java ?
Quels sont les formats d'archives supportés pour définir un module Java ?
Mise en oeuvre de modules
Définir son fichier module-info.java
Mise en oeuvre d'un module Jar
Mise en oeuvre d'un module JMod
Utilisation de modules
Utilisation d'un module Jar
Utilisation d'un module JMod
De manière simpliste, un module est un ensemble de packages Java qui vont être archivés ensembles : un module est donc relativement proche de la notion de Jar vu précédemment. Cependant un module permet de gérer de manière plus subtile les aspects suivants :
Pour qu'une archive soit comprise comme étant un module Java, il faut impérativement qu'elle contienne à sa racine un fichier module-info.java
.
Nous allons revenir dans quelques instants sur le contenu de ce fichier.
Un module Java peut être stocké dans deux types d'archives :
.jar
.
.jmod
.
Dans les deux cas, il s'agit de fichiers ZIP, mais le nouveau format JMod permet de contenir d'autres types des fichiers, outre les classiques fichiers
.class
(contenant du Byte Code Java). On peut, notamment, y trouver du code natif et des fichiers de configuration...
Nous allons maintenant nous intéresser à la mise en oeuvre d'un module. Pour ce faire nous allons considérer deux packages constituant une librairie fictive. Ces deux packages sont :
Voici une capture d'écran d'un projet mettant en oeuvre notre module et ses deux packages.
Commençons par définir le fichier de description du module. Ce fichier doit être placé à la racine de votre module (default package) et doit se nommer
module-info.java
. En voici son contenu :
1 2 3 4 5 |
module fr.koor.superlib { requires java.base; // Par défaut requires transitive java.logging; // Si l'API exposée utilise ce module, il faut aussi l'exposer ! exports fr.koor.superlib.api; } |
Le module se nommera donc fr.koor.superlib
: il est recommandé de nommer ses modules en minuscules, à l'instar des packages Java.
Notre module tire une dépendance sur le module java.base
contenant les types de bases de Java, mais cette dépendance est normalement implicite.
Il tire une seconde dépendance, transitive, sur le module java.logging
. Une dépendance transitive est exposée par le module ayant tiré cette
dépendance.
module-info.java
à sa racine).
Enfin, notre module expose le package fr.koor.superlib.api
, ce qui laisse entendre que le package fr.koor.superlib.impl
ne
sera pas visible en dehors de notre module.
Pour produire le module au format Jar, vous pouvez utiliser la commande jar[.exe]
proposée par votre JDK (Java Development Kit).
Pour de plus amples informations sur l'utilisation de cet outil, je vous renvoie vers le chapitre précédent qui traite de ce sujet.
Notez que vous pouvez aussi produire un fichier Jar à partir d'Eclipse. Pour ce faire, cliquez avec le bouton droit de la souris sur le projet puis cliquez sur « Export... », comme le montre la capture d'écran ci-dessous.
Puis sélectionnez l'assistant « JAR file », comme ci-dessous.
Il ne vous reste plus qu'à sélectionner les éléments à archiver. N'oubliez pas le fichier module-info.class
!
module-info.java
sélectionné dans l'assistant, c'est bien son .class
associé qui sera archivé.
Cela vient du fait que l'IDE Eclipse cherche à vous cacher, tant que possible, les fichiers compilés (rappelez-vous, on ne voit pas non plus le dossier
bin
dans l'explorateur de projet).
L'exécutable jmod[.exe]
, proposé depuis le Java SE 9.0, permet de gérer le nouveau format d'archives Java.
Dans une certaine mesure, l'outil est proche de l'outil jar[.exe]
bien que les options proposées ne soient pas les mêmes.
Voici, en premier lieu, l'aide en ligne proposé par la commande jmod[.exe]
.
$> jmod --help Usage: jmod (create|extract|list|describe|hash) <OPTIONS> <jmod-file> Main operation modes: create - Creates a new jmod archive extract - Extracts all the files from the archive list - Prints the names of all the entries describe - Prints the module details hash - Records hashes of tied modules. Option Description ------ ----------- -?, -h, --help Print this help message --class-path <path> Application jar files|dir containing classes --cmds <path> Location of native commands --config <path> Location of user-editable config files --dir <path> Target directory for extract --dry-run Dry run of hash mode --exclude <pattern-list> Exclude files matching the supplied comma separated pattern list, each element using one the following forms: <glob-pattern>, glob:<glob- pattern> or regex:<regex-pattern> --hash-modules <regex-pattern> Compute and record hashes to tie a packaged module with modules matching the given <regex-pattern> and depending upon it directly or indirectly. The hashes are recorded in the JMOD file being created, or a JMOD file or modular JAR on the module path specified the jmod hash command. --header-files <path> Location of header files --help-extra Print help on extra options --legal-notices <path> Location of legal notices --libs <path> Location of native libraries --main-class <String: class-name> Main class --man-pages <path> Location of man pages --module-version <module-version> Module version -p, --module-path <path> Module path --target-platform <String: target- Target platform platform> --version Version information @<filename> Read options from the specified file $>
Voici comment produire l'archive à partir des codes du projet précédent : je suis placé dans le dossier correspondant au projet et les codes sources sont
localisés dans le répertoire bin
.
$> jmod create --class-path bin fr.koor.superlib.jmod
Comme indiqué précédemment, une archive jmod est au format ZIP. De plus, une archive jmod peut contenir de fichiers .class
, bien entendu, mais
aussi d'autres types de fichiers. C'est pour cela que ce ZIP contient un dossier classes
: il contiendra les codes Java compilés.
Pour démontrer ces points je vous propose d'utiliser un outil de manipulant d'archive ZIP (dans mon cas, 7-Zip).
$> 7z l fr.koor.superlib.jmod 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 p7zip Version 16.02 (locale=fr_FR.utf8,Utf16=on,HugeFiles=on,64 bits,8 CPUs Intel(R) Core(TM) i7-4700HQ CPU @ 2.40GHz (306C3),ASM,AES-NI) Scanning the drive for archives: 1 file, 2193 bytes (3 KiB) Listing archive: fr.koor.superlib.jmod -- Path = fr.koor.superlib.jmod Type = zip Offset = 4 Physical Size = 2189 Date Time Attr Size Compressed Name ------------------- ----- ------------ ------------ ------------------------ 2020-03-29 12:50:46 ..... 243 169 classes/module-info.class 2020-03-29 12:50:46 ..... 138 116 classes/fr/koor/superlib/api/SecondaryInterface.class 2020-03-29 12:50:46 ..... 208 145 classes/fr/koor/superlib/api/EntryInterface.class 2020-03-29 12:50:46 ..... 544 331 classes/fr/koor/superlib/impl/SecondaryInterfaceImpl.class 2020-03-29 12:50:46 ..... 937 468 classes/fr/koor/superlib/impl/EntryInterfaceImpl.class ------------------- ----- ------------ ------------ ------------------------ 2020-03-29 12:50:46 2070 1229 5 files
Notez aussi que depuis le Java SE 9.0, les classes proposées en standard en Java ne sont plus stockées dans le fichier $JDK_HOME/jre/lib/rt.jar
(rt
pour RunTime). Maintenant, elles sont réparties dans différentes archives au format .jmod
. Ces archives
se trouvant dans le dossier $JDK_HOME/jmods
. Voici quelques-unes de ces archives.
Si vous avez spécifié des dépendances entre modules, lors de son exécution, la Machine Virtuelle Java (la JVM) vérifie récursivement que tous les modules sont bien présents avant d'effectivement démarrer le programme. La JVM trouve les modules à partir du « Module Path » et non plus à partir du « Class Path ».
Pour compiler, en mode ligne de commande, un module ayant lui-même une dépendance sur un autre module, veuillez procéder ainsi :
$> javac --module-path pathToYourModules src/fr/koor/client/*.java -d bin
Pour démarrer une JVM, en mode ligne de commande, qui utilise des modules Java et parmi lesquels un de ces modules définit une classe de démarrage.
$> java --module-path pathToYourModules --module fr.koor.client
Par contre, si aucun module ne définit de classe de démarrage, il faudra alors spécifier la classe de démarrage.
$> java --module-path pathToYourModules --module fr.koor.client/fr.koor.client.Start
Si vous souhaitez ajouter un module à votre projet Eclipse, veuillez demander les propriétés du projet. Ensuite, sélectionnez l'entrée
« Java Build Path » puis ajoutez le jar associé à votre module. Normalement Eclipse détecte automatiquement le fichier module-info.java
et placera l'archive dans la section dédiée aux modules.
Vous pouvez maintenant tester l'exemple suivant. Normalement, l'API doit être visible dans votre projet, alors que son implémentation interne sera non accessible. Voici un exemple de message d'erreur proposé par l'IDE Eclipse sur l'utilisation d'un package non exposé.
Un module JMod ne peut pas être utilisé directement au runtime, contrairement à un module Jar. Par contre, il peut être utilisé durant la phase de compilation ou durant une phase de link. Cette dernière possibilité est disponible depuis la version 9.0 du Java SE et sera présentée dans le prochain chapitre.
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :