Jusqu’à présent, nous avons travaillé avec des modules que nous avons écrits. Même les classes intégrées au JDK sont modularisées. Dans cette section, nous vous montrons comment utiliser des commandes pour en apprendre davantage sur les modules.
Vous n’avez pas besoin de connaître la sortie des commandes dans cette section. Vous devez, cependant, connaître la syntaxe des commandes et ce qu’elles font. Nous incluons la sortie là où cela facilite la mémorisation de ce qui se passe.
Identifier les Modules Intégrés
Le module le plus important à connaître est java.base
. Il contient la plupart des packages que vous avez appris jusqu’à présent. En fait, il est tellement important que vous n’avez même pas à utiliser la directive requires
; il est disponible pour toutes les applications modulaires. Votre fichier module-info.java
se compilera toujours si vous exigez explicitement java.base
. Cependant, c’est redondant, donc il est préférable de l’omettre. Le tableau 12.6 liste certains modules courants et ce qu’ils contiennent.
Compiler du code non modulaire | javac -cp classpath -d directory classesToCompile javac –class-path classpath -d directory classesToCompile javac -classpath classpath -d directory classesToCompile |
Exécuter du code non modulaire | java -cp classpath package.className java -classpath classpath package.className java –class-path classpath package.className |
Compiler un module | javac -p moduleFolderName -d directory classesToCompileIncludingModuleInfo javac –module-path moduleFolderName -d directory classesToCompileIncludingModuleInfo |
Exécuter un module | java -p moduleFolderName -m moduleName/package.className java –module-path moduleFolderName –module moduleName/package.className |
Décrire un module | java -p moduleFolderName -d moduleName java –module-path moduleFolderName –describe-module moduleName jar –file jarName –describe-module jar -f jarName -d |
Lister les modules disponibles | java –module-path moduleFolderName –list-modules java -p moduleFolderName –list-modules |
Afficher les dépendances | jdeps -summary –module-path moduleFolderName jarName jdeps -s –module-path moduleFolderName jarName jdeps –jdk-internals jarName jdeps -jdkinternals jarName |
Afficher la résolution des modules | java –show-module-resolution -p moduleFolderName -m moduleName java –show-module-resolution –module-path moduleFolderName –module moduleName |
Créer un JAR d’exécution | jlink -p moduleFolderName –add-modules moduleName –output zooApp jlink –module-path moduleFolderName –add-modules moduleName –output zooApp |
Le tableau 12.11 montre les options pour javac, le tableau 12.12 montre les options pour java, le tableau 12.13 montre les options pour jar, et le tableau 12.14 montre les options pour jdeps. Enfin, le tableau 12.15 montre les options pour jlink.
Option | Description |
---|---|
-cp <classpath> -classpath <classpath> –class-path <classpath> | Emplacement des JAR dans un programme non modulaire |
-d <dir> | Répertoire dans lequel placer les fichiers de classe générés |
-p <path> –module-path <path> | Emplacement des JAR dans un programme modulaire |
Option | Description |
---|---|
-p <path> –module-path <path> | Emplacement des JAR dans un programme modulaire |
-m <name> –module <name> | Nom du module à exécuter |
-d –describe-module | Décrit les détails du module |
–list-modules | Liste les modules observables sans exécuter le programme |
–show-module-resolution | Affiche les modules lors de l’exécution du programme |
Option | Description |
---|---|
-c –create | Crée un nouveau fichier JAR |
-v –verbose | Imprime les détails lors du travail avec les fichiers JAR |
-f –file | Nom du fichier JAR |
-C | Répertoire contenant les fichiers à utiliser pour créer JAR |
-d –describe-module | Décrit les détails du module |
Option | Description |
---|---|
–module-path <path> | Emplacement des JAR dans un programme modulaire |
-s -summary | Résume la sortie |
–jdk-internals -jdkinternals | Liste les utilisations des API internes |
Option | Description |
---|---|
-p –module-path <path> | Emplacement des JAR dans un programme modulaire |
–add-modules | Liste des modules à empaqueter |
–output | Nom du répertoire de sortie |
h>Nom du moduleCe qu’il contientCouverture dans le livrejava.baseCollections, math, IO, NIO.2, concurrency, etc.La plupart de ce livrejava.desktopAbstract Windows Toolkit (AWT) et SwingPas au-delà du nom du modulejava.loggingJournalisationPas au-delà du nom du modulejava.sqlJDBCChapitre 15, “JDBC”java.xmlExtensible Markup Language (XML)Pas au-delà du nom du module
Il est important de reconnaître les noms des modules fournis par le JDK. Bien que vous n’ayez pas besoin de connaître les noms par cœur, vous devez être capable de les identifier.
Vous devez savoir que les noms de modules commencent par java
pour les API que vous êtes susceptible d’utiliser et par jdk
pour les API spécifiques au JDK. Le tableau 12.7 liste tous les modules qui commencent par java
.
java.base | java.naming | java.smartcardio |
java.compiler | java.net.http | java.sql |
java.datatransfer | java.prefs | java.sql.rowset |
java.desktop | java.rmi | java.transaction.xa |
java.instrument | java.scripting | java.xml |
java.logging | java.se | java.xml.crypto |
java.management | java.security.jgss | |
java.management.rmi | java.security.sasl |
Le tableau 12.8 liste tous les modules qui commencent par jdk
. Nous recommandons de revoir cette liste pour augmenter les chances qu’ils vous semblent familiers. Rappelez-vous que vous n’avez pas à les mémoriser.
jdk.accessibility | jdk.javadoc | jdk.management.agent |
jdk.attach | jdk.jcmd | jdk.management.jfr |
jdk.charsets | jdk.jconsole | jdk.naming.dns |
jdk.compiler | jdk.jdeps | jdk.naming.rmi |
jdk.crypto.cryptoki | jdk.jdi | jdk.net |
jdk.crypto.ec | jdk.jdwp.agent | jdk.nio.mapmode |
jdk.dynalink | jdk.jfr | jdk.sctp |
jdk.editpad | jdk.jlink | jdk.security.auth |
jdk.hotspot.agent | jdk.jshell | jdk.security.jgss |
jdk.httpserver | jdk.jsobject | jdk.xml.dom |
jdk.incubator.foreign | jdk.jstatd | jdk.zipfs |
jdk.incubator.vector | jdk.localedata | |
jdk.jartool | jdk.management |
Obtenir des Détails avec java
La commande java
a trois options liées aux modules. L’une décrit un module, une autre liste les modules disponibles, et la troisième montre la logique de résolution des modules.
Il est également possible d’ajouter des modules, des exports, et plus encore en ligne de commande. Mais s’il vous plaît, ne le faites pas. C’est déroutant et difficile à maintenir. Notez que ces options sont disponibles sur java
mais pas sur toutes les commandes.
Décrire un Module
Supposons que vous ayez le fichier JAR du module zoo.animal.nourriture
et que vous souhaitiez connaître sa structure. Vous pourriez le « décompresser » et ouvrir le fichier module-info.java
. Cela vous montrerait que le module exporte un package et ne requiert explicitement aucun module.
module zoo.animal.nourriture {
exports zoo.animal.nourriture;
}
Cependant, il existe une manière plus simple. La commande java
dispose d’une option pour décrire un module. Les deux commandes suivantes sont équivalentes :
java -p mods
-d zoo.animal.nourriture
java -p mods
--describe-module zoo.animal.nourriture
Chacune affiche des informations sur le module. Par exemple, elle pourrait imprimer ceci :
zoo.animal.nourriture file:///absolutePath/mods/zoo.animal.nourriture.jar
exports zoo.animal.nourriture
requires java.base mandated
La première ligne est le module que nous avons demandé : zoo.animal.nourriture
. La deuxième ligne commence par des informations sur le module. Dans notre cas, c’est la même déclaration d’exportation de package que nous avions dans le fichier de déclaration de module.
Sur la troisième ligne, nous voyons requires java.base mandated
. Attendez une minute. La déclaration de module ne spécifie clairement aucun module dont zoo.animal.nourriture
dépend.
Rappelez-vous, le module java.base
est spécial. Il est automatiquement ajouté comme dépendance à tous les modules. Ce module contient des packages fréquemment utilisés comme java.util
. C’est ce que signifie mandated
. Vous obtenez java.base
que vous l’ayez demandé ou non.
Dans les classes, le package java.lang
est automatiquement importé, que vous le tapiez ou non. Le module java.base
fonctionne de la même manière. Il est automatiquement disponible pour tous les autres modules.
Plus d’informations sur la description des modules
Vous devez seulement savoir comment exécuter --describe-module
plutôt que d’interpréter la sortie. Cependant, vous pourriez rencontrer quelques surprises lors de l’expérimentation avec cette fonctionnalité, nous les décrivons donc un peu plus en détail ici.
Supposons que les éléments suivants sont le contenu de module-info.java
dans zoo.animal.soins
:
module zoo.animal.soins {
exports zoo.animal.soins.medical to zoo.staff;
requires transitive zoo.animal.nourriture;
}
Maintenant, nous avons la commande pour décrire le module et la sortie.
java -p mods -d zoo.animal.soins
zoo.animal.soins file:///absolutePath/mods/zoo.animal.soins.jar
requires zoo.animal.nourriture transitive
requires java.base mandated
qualified exports zoo.animal.soins.medical to zoo.staff
contains zoo.animal.soins.details
La première ligne de la sortie est le chemin absolu du fichier de module. Les deux lignes requires
devraient sembler familières également. La première est dans le module-info
, et l’autre est ajoutée à tous les modules. Ensuite vient quelque chose de nouveau. Le qualified exports
est le nom complet du package que nous exportons vers un module spécifique.
Enfin, le contains
signifie qu’il y a un package dans le module qui n’est pas exporté du tout. C’est vrai. Notre module a deux packages, et l’un n’est disponible que pour le code à l’intérieur du module.
Lister les Modules Disponibles
En plus de décrire les modules, vous pouvez utiliser la commande java
pour lister les modules qui sont disponibles. La forme la plus simple liste les modules qui font partie du JDK.
java --list-modules
Lorsque nous l’avons exécutée, la sortie s’est étendue sur 70 lignes et ressemblait à ceci :
java.base@17
java.compiler@17
java.datatransfer@17
Il s’agit d’une liste de tous les modules qui viennent avec Java et leurs numéros de version. Vous pouvez dire que nous utilisions Java 17 lors du test de cet exemple.
Plus intéressant, vous pouvez utiliser cette commande avec du code personnalisé. Essayons à nouveau avec un répertoire contenant nos modules zoo.
java -p mods --list-modules
Combien de lignes attendez-vous dans la sortie cette fois ? Il y a 78 lignes maintenant : les 70 modules intégrés plus les 8 que nous avons créés dans ce chapitre. Deux des lignes personnalisées ressemblent à ceci :
zoo.animal.soins file:///absolutePath/mods/zoo.animal.soins.jar
zoo.animal.nourriture file:///absolutePath/mods/zoo.animal.nourriture.jar
Puisqu’il s’agit de modules personnalisés, nous obtenons un emplacement sur le système de fichiers. Si le projet avait un numéro de version de module, il aurait à la fois le numéro de version et le chemin du système de fichiers.
Notez que --list-modules
se termine dès qu’il imprime les modules observables. Il n’exécute pas le programme.
Afficher la Résolution des Modules
Si la liste des modules ne vous donne pas assez de sortie, vous pouvez également utiliser l’option --show-module-resolution
. Vous pouvez la considérer comme un moyen de déboguer les modules. Elle affiche beaucoup de sortie lorsque le programme démarre. Puis elle exécute le programme.
java --show-module-resolution
-p nourriture
-m zoo.animal.nourriture/zoo.animal.nourriture.Tache
Heureusement, vous n’avez pas besoin de comprendre cette sortie. Cela dit, l’avoir vu facilitera la mémorisation. Voici un extrait de la sortie :
root zoo.animal.nourriture file:///absolutePath/nourriture/
java.base binds java.desktop jrt:/java.desktop
java.base binds jdk.jartool jrt:/jdk.jartool
…
jdk.security.auth requires java.naming jrt:/java.naming
jdk.security.auth requires java.security.jgss jrt:/java.security.jgss
…
Tout nourri !
Elle commence par lister le module racine. C’est celui que nous exécutons : zoo.animal.nourriture
. Ensuite, elle liste de nombreuses lignes de packages inclus par le module java.base
obligatoire. Après un moment, elle liste les modules qui ont des dépendances. Enfin, elle affiche le résultat du programme : Tout nourri !
Décrire avec jar
Comme la commande java
, la commande jar
peut décrire un module. Ces commandes sont équivalentes :
jar -f mods/zoo.animal.nourriture.jar -d
jar --file mods/zoo.animal.nourriture.jar --describe-module
La sortie est légèrement différente de celle que nous avons obtenue lorsque nous avons utilisé la commande java
pour décrire le module. Avec jar
, elle affiche ce qui suit :
zoo.animal.nourriture jar:file:///absolutePath/mods/zoo.animal.nourriture.jar
/!module-info.class
exports zoo.animal.nourriture
requires java.base mandated
La version JAR inclut le module-info.class
dans le nom de fichier, ce qui n’est pas une différence particulièrement significative. Vous devez savoir que les deux commandes peuvent décrire un module.
Apprendre les Dépendances avec jdeps
La commande jdeps
vous donne des informations sur les dépendances au sein d’un module. Contrairement à la description d’un module, elle examine le code en plus de la déclaration du module. Cela vous indique quelles dépendances sont réellement utilisées plutôt que simplement déclarées.
Vous êtes censé comprendre comment utiliser jdeps
avec des projets qui n’ont pas encore été modularisés pour aider à identifier les dépendances et les problèmes. Tout d’abord, nous allons créer un fichier JAR à partir de cette classe. Si vous suivez, n’hésitez pas à copier la classe à partir des exemples en ligne référencés au début du chapitre plutôt que de la taper.
// Animatronique.java
package zoo.dinos;
import java.time.*;
import java.util.*;
import sun.misc.Unsafe;
public class Animatronique {
private List noms;
private LocalDate dateVisite;
public Animatronique(List noms, LocalDate dateVisite) {
this.noms = noms;
this.dateVisite = dateVisite;
}
public void methodeNonSecurisee() {
Unsafe unsafe = Unsafe.getUnsafe();
}
}
Cet exemple est idiot. Il utilise un certain nombre de classes sans rapport. Le Zoo du Bronx avait vraiment des dinosaures électroniques pendant un moment, donc au moins l’idée d’avoir des dinosaures dans un zoo n’est pas au-delà du domaine du possible.
Maintenant, nous pouvons compiler ce fichier. Vous avez peut-être remarqué qu’il n’y a pas de fichier module-info.java
. C’est parce que nous ne créons pas un module. Nous examinons les dépendances dont nous aurons besoin lorsque nous modulariserons ce JAR.
javac zoo/dinos/*.java
La compilation fonctionne, mais elle vous donne quelques avertissements concernant Unsafe
étant une API interne. Ne vous inquiétez pas pour l’instant — nous en discutons bientôt. (Peut-être que les dinosaures ont disparu parce qu’ils ont fait quelque chose de non sécurisé.)
Ensuite, nous créons un fichier JAR.
jar -cvf zoo.dino.jar .
Nous pouvons exécuter la commande jdeps
contre ce JAR pour en apprendre davantage sur ses dépendances. Tout d’abord, exécutons la commande sans options. Sur les deux premières lignes, la commande imprime les modules que nous devrions ajouter avec une directive requires
pour migrer vers le système de modules. Elle imprime également un tableau montrant quels packages sont utilisés et à quels modules ils correspondent.
jdeps zoo.dino.jar
zoo.dino.jar -> java.base
zoo.dino.jar -> jdk.unsupported
zoo.dinos -> java.lang java.base
zoo.dinos -> java.time java.base
zoo.dinos -> java.util java.base
zoo.dinos -> sun.misc JDK internal API (jdk.unsupported)
Notez que java.base
est toujours inclus. Il indique également quels modules contiennent les classes utilisées par le JAR. Si nous exécutons en mode résumé, nous ne voyons que la première partie où jdeps
liste les modules. Il existe deux formats pour le drapeau de résumé :
jdeps -s zoo.dino.jar
jdeps -summary zoo.dino.jar
zoo.dino.jar -> java.base
zoo.dino.jar -> jdk.unsupported
Pour un projet réel, la liste des dépendances pourrait inclure des dizaines, voire des centaines de packages. Il est utile de voir le résumé des modules uniquement. Cette approche facilite également la visualisation de la présence de jdk.unsupported
dans la liste.
Il existe également une option --module-path
que vous pouvez utiliser si vous souhaitez rechercher des modules en dehors du JDK. Contrairement à d’autres commandes, il n’y a pas de forme abrégée pour cette option sur jdeps
.
Vous avez peut-être remarqué que jdk.unsupported
ne figure pas dans la liste des modules que vous avez vue dans le tableau 12.8. Il est spécial car il contient des bibliothèques internes que les développeurs des versions précédentes de Java étaient découragés d’utiliser, bien que beaucoup aient ignoré cet avertissement. Vous ne devriez pas y faire référence, car il pourrait disparaître dans les futures versions de Java.
Utilisation du drapeau –jdk-internals
La commande jdeps
a une option pour fournir des détails sur ces API non supportées. La sortie ressemble à ceci :
jdeps --jdk-internals zoo.dino.jar
zoo.dino.jar -> jdk.unsupported
zoo.dinos.Animatronique -> sun.misc.Unsafe
JDK internal API (jdk.unsupported)
Warning:
JDK Internal API Suggested Replacement
---------------- ---------------------
sun.misc.Unsafe See http://openjdk.java.net/jeps/260
L’option --jdk-internals
liste toutes les classes que vous utilisez qui appellent une API interne ainsi que l’API concernée. À la fin, elle fournit un tableau suggérant ce que vous devriez faire à ce sujet. Si vous avez écrit le code appelant l’API interne, ce message est utile. Sinon, le message serait utile pour l’équipe qui a écrit le code. Vous, en revanche, pourriez avoir besoin de mettre à jour ou de remplacer entièrement ce fichier JAR par un qui résout le problème. Notez que -jdkinternals
est équivalent à --jdk-internals
.
Scénario du Monde Réel
À propos de sun.misc.Unsafe
Avant le Système de Module de la Plateforme Java, les classes devaient être public
si vous vouliez qu’elles soient utilisées en dehors du package. Il était raisonnable d’utiliser la classe dans le code JDK puisqu’il s’agit de code de bas niveau déjà étroitement couplé au JDK. Comme elle était nécessaire dans plusieurs packages, la classe a été rendue public
. Sun l’a même nommée Unsafe
, pensant que cela empêcherait quiconque de l’utiliser en dehors du JDK.
Cependant, les développeurs sont astucieux et ont utilisé la classe puisqu’elle était disponible. Un certain nombre de bibliothèques open source largement utilisées ont commencé à utiliser Unsafe
. Bien qu’il soit très peu probable que vous utilisiez cette classe dans votre projet directement, vous utilisez probablement une bibliothèque open source qui l’utilise.
La commande jdeps
vous permet d’examiner ces JAR pour voir si vous aurez des problèmes lorsqu’Oracle empêchera finalement l’utilisation de cette classe. Si vous trouvez des utilisations, vous pouvez vérifier s’il existe une version plus récente du JAR que vous pouvez mettre à niveau.
Utilisation des Fichiers Module avec jmod
La dernière commande que vous devez connaître est jmod
. Vous pourriez penser qu’un JMOD est un fichier de module Java. Pas tout à fait. Oracle recommande d’utiliser des fichiers JAR pour la plupart des modules. Les fichiers JMOD ne sont recommandés que lorsque vous avez des bibliothèques natives ou quelque chose qui ne peut pas aller dans un fichier JAR. Cela est peu susceptible de vous affecter dans le monde réel.
La chose la plus importante à retenir est que jmod
ne sert qu’à travailler avec les fichiers JMOD. Le tableau 12.9 liste les modes courants.
Opération | Description |
---|---|
create | Crée un fichier JMOD. |
extract | Extrait tous les fichiers du JMOD. Fonctionne comme la décompression. |
describe | Imprime les détails du module tels que requires. |
list | Liste tous les fichiers dans le fichier JMOD. |
hash | Imprime ou enregistre les hachages. |
Création d’environnements d’exécution Java avec jlink
L’un des avantages des modules est de pouvoir fournir uniquement les parties de Java dont vous avez besoin. Notre exemple de zoo du début du chapitre n’a pas beaucoup de dépendances. Si l’utilisateur n’a pas déjà Java ou est sur un appareil sans beaucoup de mémoire, télécharger un JDK de plus de 150 Mo est une grosse demande. Voyons à quel point le package doit réellement être gros ! Cette commande crée notre distribution plus petite :
jlink --module-path mods --add-modules zoo.animal.talks --output zooApp
D’abord, nous spécifions où trouver les modules personnalisés avec -p
ou --module-path
. Ensuite, nous spécifions nos noms de modules avec --add-modules
. Cela inclura les dépendances tant qu’elles peuvent être trouvées. Enfin, nous spécifions le nom du dossier de notre JDK plus petit avec --output
.
Le répertoire de sortie contient les répertoires bin
, conf
, include
, legal
, lib
et man
ainsi qu’un fichier de version. Ceux-ci devraient vous sembler familiers car vous les trouvez également dans le JDK complet.
Lorsque nous exécutons cette commande et compressons le répertoire zooApp
, le fichier ne fait que 15 Mo. C’est d’un ordre de grandeur plus petit que le JDK complet. D’où vient cette économie d’espace ? Il y a de nombreux modules dans le JDK dont nous n’avons pas besoin. De plus, les outils de développement comme javac
n’ont pas besoin d’être dans une distribution d’exécution.
Il y a beaucoup plus d’éléments pour personnaliser ce processus. Par exemple, vous pouvez sauter la génération de la documentation d’aide et économiser encore plus d’espace.