Comment fonctionnent les différents types de modules Java ?

Tous les modules que nous avons utilisés jusqu’à présent dans ce chapitre sont appelés modules nommés. Il existe deux autres types de modules : les modules automatiques et les modules sans nom. Dans cette section, nous décrivons ces trois types de modules.

Modules Nommés

Un module nommé est un module contenant un fichier module-info.java. Pour rappel, ce fichier apparaît à la racine du JAR aux côtés d’un ou plusieurs packages. Sauf indication contraire, un module est un module nommé. Les modules nommés apparaissent sur le chemin de module plutôt que sur le classpath. Plus tard, vous apprendrez ce qui se passe si un JAR contenant un fichier module-info.java se trouve sur le classpath. Pour l’instant, sachez simplement qu’il n’est pas considéré comme un module nommé car il ne se trouve pas sur le chemin de module.

Pour vous aider à retenir ce concept, un module nommé possède le nom à l’intérieur du fichier module-info.java et se trouve sur le chemin de module.

Rappelons que la seule façon pour que les sous-classes des classes scellées se trouvent dans un package différent est qu’elles soient dans le même module nommé.

Modules Automatiques

Un module automatique apparaît sur le chemin de module mais ne contient pas de fichier module-info.java. C’est simplement un fichier JAR ordinaire qui est placé sur le chemin de module et est traité comme un module.

Pour vous aider à retenir ce concept, Java détermine automatiquement le nom du module. Le code référençant un module automatique le traite comme s’il y avait un fichier module-info.java présent. Il exporte automatiquement tous les packages. Il détermine également le nom du module. Comment détermine-t-il le nom du module, demandez-vous ? Excellente question.

Pour y répondre, nous devons fournir un peu d’histoire sur les fichiers JAR et l’adoption des modules. Chaque fichier JAR contient un dossier spécial appelé META-INF et, à l’intérieur, un fichier texte appelé MANIFEST.MF. Il peut être créé automatiquement lors de la création du JAR ou à la main par l’auteur du JAR. Pour revenir aux modules, de nombreuses bibliothèques Java n’étaient pas tout à fait prêtes à se modulariser lorsque la fonctionnalité a été introduite. Les auteurs ont été encouragés à déclarer le nom qu’ils avaient l’intention d’utiliser pour le module en ajoutant une propriété nommée Automatic-Module-Name dans leur fichier MANIFEST.MF.

À propos du fichier MANIFEST.MF

Un fichier JAR contient un fichier texte spécial appelé META-INF/MANIFEST.MF qui contient des informations sur le JAR. Il existe depuis bien plus longtemps que les modules—depuis les premiers jours de Java et des JAR, pour être exact. La figure montre comment le manifeste s’intègre dans la structure de répertoire d’un fichier JAR.

Le manifeste contient des informations supplémentaires sur le fichier JAR. Par exemple, il contient souvent la version de Java utilisée pour construire le fichier JAR. Pour les programmes en ligne de commande, la classe avec la méthode main() est généralement spécifiée.

Chaque ligne du manifeste est une paire clé/valeur séparée par deux points. Vous pouvez considérer le manifeste comme une carte de noms et de valeurs de propriétés. Le manifeste par défaut dans Java 17 ressemble à ceci :

Manifest-Version: 1.0
Created-By: 17 (Oracle Corporation)

Spécifier une seule propriété dans le manifeste a permis aux fournisseurs de bibliothèques de faciliter les choses pour les applications qui voulaient utiliser leur bibliothèque dans une application modulaire. Vous pouvez le considérer comme une promesse que lorsque la bibliothèque deviendra un module nommé, elle utilisera le nom de module spécifié.

Si le fichier JAR ne spécifie pas de nom de module automatique, Java vous permettra quand même de l’utiliser dans le chemin de module. Dans ce cas, Java déterminera le nom du module pour vous. Nous dirions que cela se produit automatiquement, mais la blague est probablement usée maintenant.

Java détermine le nom du module automatique en le basant sur le nom de fichier du JAR. Passons en revue les règles en commençant par un exemple. Supposons que nous ayons un fichier JAR nommé calendrier-fetes-1.0.0.jar.

D’abord, Java supprimera l’extension .jar du nom. Ensuite, Java supprimera la version de la fin du nom du fichier JAR. C’est important car nous voulons que les noms des modules soient cohérents. Avoir un nom de module automatique différent chaque fois que vous passez à une nouvelle version ne serait pas bon ! Après tout, cela vous obligerait à changer la déclaration de module de votre application bien propre et modularisée chaque fois que vous intégreriez une version plus récente du JAR calendrier des fêtes.

La suppression de la version et de l’extension nous donne calendrier-fetes. Cela nous laisse avec un problème. Les tirets (-) ne sont pas autorisés dans les noms de modules. Java résout ce problème en convertissant tous les caractères spéciaux du nom en points (.). Par conséquent, le nom du module est calendrier.fetes. Tous les caractères autres que les lettres et les chiffres sont considérés comme des caractères spéciaux dans ce remplacement. Enfin, les points adjacents ou les points en début/fin sont supprimés.

Puisqu’il s’agit d’un certain nombre de règles, passons en revue l’algorithme dans une liste pour déterminer le nom d’un module automatique :

  • Si le MANIFEST.MF spécifie un Automatic-Module-Name, utilisez-le. Sinon, procédez avec les règles restantes.
  • Supprimez l’extension du fichier du nom du JAR.
  • Supprimez toute information de version de la fin du nom. Une version est composée de chiffres et de points avec d’éventuelles informations supplémentaires à la fin : par exemple, -1.0.0 ou -1.0-RC.
  • Remplacez tous les caractères restants autres que les lettres et les chiffres par des points.
  • Remplacez toutes les séquences de points par un seul point.
  • Supprimez le point s’il est le premier ou le dernier caractère du résultat.

Le tableau 12.16 montre comment appliquer ces règles à deux exemples où aucun nom de module automatique n’est spécifié dans le manifeste.

#DescriptionExemple 1Exemple 2
1Nom du JAR de départcommons2-x-1.0.0-SNAPSHOT.jarmod_$-1.0.jar
2Supprimer l’extension du fichiercommons2-x-1.0.0-SNAPSHOTmod_$-1.0
3Supprimer les informations de versioncommons2-xmod_$
4Remplacer les caractères spéciauxcommons2.xmod..
5Remplacer la séquence de pointscommons2.xmod.
6Supprimer les points en début/fin (résulte en nom de module automatique)commons2.xmod

Bien que l’algorithme de création de noms de modules automatiques fasse de son mieux, il ne peut pas toujours trouver un bon nom. Par exemple, 1.2.0-calendrier-1.2.2-bon-1.jar n’est pas propice.

Modules Sans Nom

Un module sans nom apparaît sur le classpath. Comme un module automatique, c’est un JAR ordinaire. Contrairement à un module automatique, il se trouve sur le classpath plutôt que sur le chemin de module. Cela signifie qu’un module sans nom est traité comme du code ancien et un citoyen de seconde classe pour les modules.

Un module sans nom ne contient généralement pas de fichier module-info.java. S’il en contient un par hasard, ce fichier sera ignoré puisqu’il se trouve sur le classpath.

Les modules sans nom n’exportent aucun package vers les modules nommés ou automatiques. Le module sans nom peut lire depuis n’importe quels JAR sur le classpath ou le chemin de module. Vous pouvez considérer un module sans nom comme du code qui fonctionne comme Java fonctionnait avant les modules. Oui, nous savons qu’il est déroutant pour quelque chose qui n’est pas vraiment un module d’avoir le mot module dans son nom.

Révision des Types de Modules

Étudiez le tableau 12.17 en profondeur. Un point clé à retenir est que le code sur le classpath peut accéder au chemin de module. En revanche, le code sur le chemin de module ne peut pas lire depuis le classpath.

PropriétéNomméAutomatiqueSans nom
Un module ______ contient-il un fichier module-info.java ?OuiNonIgnoré si présent
Quels packages un module ______ exporte-t-il vers d’autres modules ?Ceux dans le fichier module-info.javaTous les packagesAucun package
Un module ______ est-il lisible par d’autres modules sur le chemin de module ?OuiOuiNon
Un module ______ est-il lisible par d’autres JAR sur le classpath ?OuiOuiOui