UP | HOME

Des modules pour la compilation et l'exécution

Table of Contents

1 Avant Java 9 : une modularité insuffisante

Situation avant Java 9

  • Aucun module avant Java 9
  • Cependant, possibilité d'utiliser des archives de code java au format zip (sauvegardées dans des fichiers de suffixe .jar, pour java archive)
    • Possibilité de réaliser une archive à partir du code exécutable par la machine virtuelle Java, à la suite d'une compilation
    • Possibilité de compiler un projet en utilisant des archives Java préalablement importées
    • Possibilité de lancer la machine virtuelle avec la spécification d'un ensemble d'archives Java

Quelques complications connues avant Java 9

  • Dépendances non exprimées ente les archives Java
    • Origine : le format jar ne permet pas d'exprimer les dépendances dans les méta-données associées à l'archive (via un fichier manifeste).
    • Solution : utiliser des outils comme Maven, permettant la gestion et la production automatisées de projets Java (outils dits de "build")
      A -- requiert -> B -- requiert -> C
      		   -- requiert -> D
        -- requiert -> E ...
      
  • Ambiguïté de nommage (un nom qualifié relié à plusieurs définitions et non une seule) à l'exécution
    • Origine : multiples définitions difficiles à identifier (spécification de X.jar et de Y.jar, avec X.jar incluant Y.jar)
      app -- requiert -> X.jar -- inclut -> Y.jar
          -- requiert -> Y.jar (conflit !)
      
    • Solution : sans modules, aucune solution, sauf la discipline des développeurs
  • Conflits de versions
    • Origine : utilisation (par transitivité) d'une même archive, avec des versions différentes incompatibles
      app -- requiert -> X.jar -- requiert -> Z.jar (version 1)
          -- requiert -> Y.jar -- requiert -> Z.jar (version 2, incompatible avec version 1)
      
    • Solution : sans modules, aucune solution, sauf l'implémentation spécifique d'un chargeur de classes ("class loader") (ce qui est compliqué)
  • Contrôle d'accès limité ente archives Java
    • Origine : le seul contrôle d'accès se fait au niveau des paquets de Java ("package"), avec la visibilité public, autorisant l'accès de tout paquet, ou la visibilité privée (défaut), autorisant l'accès du seul paquet de définition.
    • Solution : aucune. La bonne pratique est de regrouper dans un paquet une interface et une fabrique publiques et des classes d'implémentations privées. Si l'implémentation n'est pas dans le même paquet, elle doit être déclarée publique et devient ainsi accessible de tout paquet. Pour des questions de sécurité, il peut être nécessaire de réaliser des contrôles dynamiquement, en utilisant un gestionnaire de sécurité et une inspection de la pile des appels à l'exécution.
      +------------------+
      |    paquet p      |
      |------------------|
      |+ interface       |
      |+ fabriques       |
      |- implémentations |
      +------------------+
      
  • Lenteur au démarrage
    • Origine : bien que le chargement des classes se fasse de manière paresseuse (au premier usage, et non dès le lancement), il est nécessaire de parcourir l'ensemble des archives pour rechercher une classe, sans indication pour accélérer la recherche.

    Solution : sans modules, aucune solution.

  • Taille de la machine virtuelle
    • Origine : la machine virtuelle est accompagnée de nombreuses bibliothèques, sans possibilité de se restreindre à un sous-ensemble réellement utilisé. Pour des machines aux ressources limitées, c'est un problème.
    • Solution : sans modules, aucune.

2 Le système de modules introduit avec Java 9

Buts poursuivis

  • Fiabilité des configurations
  • Contrôle de l'accessibilité pour une meilleure sécurité et maintenanbilité
  • Meilleures performances (au démarrage)
  • Plateforme modulaire

Buts non poursuivis

  • Aucune version prise en compte
  • Aucun téléchargement à partir d'un dépôt central
  • Bref, pas de remplacement d'un outil comme maven

De nouveaux outils

  • De nouvelles options
    > java --list-modules
    > java --describe-module unModule
    

    Le graphe des modules

  • Une compilation prenant en compte les modules (en plus des classes)
    > javac --module-path repertoire
    
  • Une exécution prenant en compte les modules (en plus des classes)
    > java --module-path repertoire
    

Un manifeste : module-info.java à la racine du code source d'un projet

module nomModule { // 'nomModule' peut utiliser une notation pointée pour éviter les ambiguïtés.
  requires nomModule1; // 'nomModule' dépend de 'nomModule1'.
  requires transitive nomModule2; // 'nomModule' dépend de 'nomModule1' et 
				  //   tout module dépendant de 'nomModule' dépend de 'nomModule1'.
  exports nomPaquet; // le paquet 'nomPaquet' de 'nomModule' est accessible à tout module requérant 'nomModule'.
  • Déclaration des dépendances entre modules (requires)
    • Bonne pratique : un graphe de dépendances simple, obligatoirement sans circuit
  • Définition de l'interface visible sous la forme de paquets (exports)
    • Bonne pratique : exportation d'un paquet unique contenant les types (interfaces Java) et les fabriques (permettant de construire des objets appartenant aux types)

Compilation et production d'archives Java (en première approximation)

  • Sans dépendance
    > javac -d bin ...
    > jar --create --file mods/...
    
  • Avec dépendance
    > javac --module-path mods ...
    > jar --create --file mods/...
    
  • Avec dépendance et importation de bibliotheques (sous la forme d'archives)
    > javac --module-path mods:bibs ...
    > jar --create --file mods/...
    

3 Travaux pratiques

Sous Eclipse, ce qui permet de bénéficier d'une interface graphique pour les commandes de compilatio ou de production d'archives

Un projet Maven

  • Créer un nouveau projet et le configurer en projet Maven.
  • Déclarer dans le fichier pom.xml les dépendances suivantes.
    <dependencies>
      <dependency>
        <groupId>org.eclipse.collections</groupId>
        <artifactId>eclipse-collections-api</artifactId>
        <version>10.2.0</version>
      </dependency>
      <dependency>
        <groupId>org.eclipse.collections</groupId>
        <artifactId>eclipse-collections</artifactId>
        <version>10.2.0</version>
      </dependency>
    </dependencies>
    

Deux modules

  • Créer deux projets, correspondant à deux modules.
  • Définir dans le premier deux paquets et des classes dans chaque paquet.
  • Exporter l'un des paquets.
  • Dans le second projet, importer le premier module. Créer un paquet et une classe applicative dans ce paquet. Y utiliser les classes du premier module.
  • Produire une archive pour le premier module.
  • L'utiliser dans le second module.

4 Documentation

Author: grall
Version history: v1: 2020-03-10.
Comments or questions: Send a mail.
The webpage content is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.