1. Qu’est-ce que module-info.java et où se trouve-t-il ?
Un module en Java commence toujours par le fichier module-info.java. C’est en quelque sorte le « passeport » du module, où vous déclarez son nom, les packages exportés et ses dépendances vers d’autres modules.
Où le trouver ?
Le fichier module-info.java doit se trouver à la racine du dossier des sources de votre module. Par exemple, si votre projet a la structure suivante :
project-root/
└── src/
└── my.module.name/
├── module-info.java
└── com/
└── example/
└── api/
└── MyClass.java
Ici, my.module.name — c’est le nom du module (nous verrons les règles de nommage un peu plus loin).
Sans ce fichier, un module n’est tout simplement pas un module !
S’il est absent — c’est un simple « ancien » projet Java, même s’il comporte une foule de dossiers et de classes.
2. Syntaxe de module-info.java : éléments de base
Voyons tout de suite l’exemple du fichier le plus simple — rien d’effrayant, promis !
module my.module.name {
exports com.example.api;
requires java.sql;
}
Décortiquons-le morceau par morceau :
module my.module.name { ... }
C’est la déclaration d’un module nommé my.module.name. Le nom du module est un identifiant unique, qui correspond généralement au package racine (par exemple, com.example.app).
Fait amusant : si vous nommez votre module java.base, le compilateur ne sera pas ravi. Évitez d’essayer de remplacer les modules standard.
exports com.example.api;
Cette ligne dit : « Moi, le module, je suis prêt à partager tout ce qui se trouve dans le package com.example.api ». Tout ce qui se trouve dans ce package et est marqué public sera visible des autres modules. Tout le reste — strictement interne.
requires java.sql;
Ici, nous avouons honnêtement au compilateur : « Pour fonctionner, j’ai besoin du module standard java.sql ». Sans cela, le compilateur ne nous laissera pas utiliser les classes de ce module.
Mots-clés supplémentaires (pour votre culture générale)
- opens <package>; — ouvre le package à la réflexion (par exemple pour des bibliothèques de sérialisation comme Jackson).
- uses <service-interface>; — indique que le module utilise un service (interface).
- provides <service-interface> with <implementation-class>; — indique que le module fournit une implémentation du service.
Dans ce cours, nous nous concentrerons sur exports et requires — ils sont nécessaires dans l’immense majorité des projets d’apprentissage et en production.
3. Exemples de module-info.java
Exemple 1. Module minimal
module com.example.hello {
exports com.example.hello.api;
}
- Ce module n’exporte que le package com.example.hello.api.
- Tout ce qui se trouve, par exemple, dans com.example.hello.internal sera caché aux autres modules, même s’il y a des public-classes.
Exemple 2. Module avec dépendance
module com.example.dbclient {
exports com.example.db.api;
requires java.sql;
}
Nous pouvons utiliser JDBC, mais uniquement parce que nous avons honnêtement déclaré la dépendance à java.sql.
Exemple 3. Plusieurs packages exportés
module com.example.library {
exports com.example.library.api;
exports com.example.library.utils;
}
On peut exporter autant de packages que nécessaire (mais il ne faut pas tout exporter — c’est justement le sens des modules !).
4. Contraintes et règles
Nom du module
- Correspond généralement au package racine (par exemple, com.example.app).
- Ne doit pas coïncider avec les noms des modules standard (java.base, java.sql, etc.).
- Ne doit pas contenir d’espaces, de caractères spéciaux, ni commencer par un chiffre, etc.
- Recommandation : utilisez le nom de domaine inversé de votre organisation ou de votre projet afin d’éviter les conflits.
Un module — un seul module-info.java
Un module ne peut contenir qu’un seul fichier de ce type. S’il y en a deux — le compilateur vous fera un « scandale modulaire ».
Export d’un package par un seul module
Un même package ne peut pas être exporté par deux modules différents. C’est comme avoir deux passeports au même nom — l’État n’approuvera pas.
Packages à l’intérieur du module
Vous ne pouvez exporter que les packages qui existent réellement dans la structure des sources de ce module. L’export d’un package inexistant entraînera une erreur de compilation.
5. Pratique : créer un module-info.java dans le projet
Supposons que nous ayons un projet simple avec le contenu suivant :
project-root/
└── src/
└── com.example.greetings/
├── module-info.java
└── com/
└── example/
└── greetings/
├── api/
│ └── Greeter.java
└── internal/
└── SecretSauce.java
Étape 1. Créer module-info.java
module com.example.greetings {
exports com.example.greetings.api;
}
Étape 2. Essayer d’utiliser une classe du package internal dans un autre module
Supposons que nous ayons un second module com.example.app, qui souhaite accéder à SecretSauce :
module com.example.app {
requires com.example.greetings;
}
import com.example.greetings.internal.SecretSauce; // ERREUR !
Résultat :
Le compilateur dira : « Le package com.example.greetings.internal n’est pas exporté par le module com.example.greetings ». Même si la classe SecretSauce est public, elle n’est pas accessible aux autres modules.
C’est cela, une véritable encapsulation au niveau des modules !
Étape 3. Essayer de ne pas déclarer requires
Si, dans com.example.app, nous n’écrivons pas requires com.example.greetings;, et que nous essayons d’utiliser une classe de com.example.greetings.api, le compilateur signalera une erreur :
package com.example.greetings.api is not visible
6. Erreurs courantes lors de l’utilisation de module-info.java
Erreur n° 1 : discordance entre le nom du module et la structure du projet.
Si vous nommez le module com.example.app, mais que la structure des dossiers est src/main/java/app, le compilateur ne comprendra pas ce que vous attendez de lui. Le nom du module correspond généralement au package racine, et l’arborescence doit le refléter.
Erreur n° 2 : tout exporter indistinctement.
N’exportez que ce qui doit réellement être visible par les autres modules. N’écrivez pas exports com.example; juste parce que « c’est plus simple ». Cela viole l’encapsulation.
Erreur n° 3 : avoir oublié d’ajouter requires.
Si vous utilisez des classes d’un autre module ou de la bibliothèque standard (par exemple, java.sql) mais que vous oubliez de déclarer la dépendance — vous obtiendrez une erreur de compilation.
Erreur n° 4 : package inexistant dans exports.
Si vous écrivez exports com.example.foo;, mais que ce package n’existe pas — le compilateur dira que vous « exportez du vent ».
Erreur n° 5 : classes publiques dans un package non exporté.
Si une classe est déclarée public mais se trouve dans un package qui n’est pas exporté, cette classe ne sera visible qu’à l’intérieur du module. Ce n’est pas une erreur, mais cela surprend souvent les débutants.
Erreur n° 6 : plusieurs module-info.java dans un même module.
Un module ne doit contenir qu’un seul fichier module-info.java. S’il y en a deux — le compilateur ne pourra pas construire le projet.
GO TO FULL VERSION