• 40 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 24/04/2019

Créez une JVM modulaire avec Java 9

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Avec cette version de Java, l'écosystème de ce langage a radicalement changé. Avant, le langage était contenu dans quelques jar et bibliothèques assez grosses :   rt.jar  qui contient une grande partie du langage faisait près de 53 Mo. Maintenant le langage est découpé en modules ce qui a pour effet de :

  • réduire la taille physique du JRE qui peut poser problème notamment pour l'embarquer sur des équipements avec de l'espace disque et mémoire limités.

  • augmenter la sécurité des applications car le découpage réduit la surface d'attaque pour des personnes malveillantes.

Cette version du langage a également un autre effet important, elle rajoute un niveau de portée pour vos objets. Lorsque vous livrez un module, seuls les objets spécifiés pourront être accessibles, même si toutes les classes de votre modules ont une portée «public».

Quelques fait sur la JVM

Avant de faire votre premier module, regardons de plus près les changement sur notre environnement. Comme je vous le disais dans l'introduction, avant Java 9, le gros du langage se trouvait dans le fichier rt.jar. Celui-ci se trouvait dans le répertoire « jre » du JDK. Il y avait bien sûr d'autres fichiers importants comme tools.jarqui, lui, était dans le dossier « lib » du JDK. Ces fichiers étaient très volumineux et tendait à grossir avec les évolutions du langage mais c'est maintenant le fin du règne de ces fichiers et vient à présent l’avènement des modules. L'image ci-dessous montre les répertoires JDK8 et JDK9.

Différence entre Java 8 et Java 9
Différence entre Java 8 et Java 9

Vous pouvez constater que le dossier « jre » quui contenait une très grande partie du langage a disparu et est remplacé par un dossier « jmods ». Regardons ce que nous pouvons trouver dedans.

Contenu du dossier jmods de Java 9
Contenu du dossier jmods de Java 9

Il y a pléthore de fichiers avec l'extension «   .jmod   », ce sont en fait des fichiers compressés que vous pouvez facilement ouvrir avec un outil comme 7zip. Il y a d'ailleurs un fichier qui semble intéressant « java.base.jmod », ouvrons-le pour voir. Il y a pas mal de dossiers mais je vais attirer votre attention sur un d'entre eux. Regardez dans le dossier «   classes/java  ».

Contenu d'un module Java 9
Contenu d'un module Java 9

Ces noms doivent vous être familier maintenant. Oui, nous sommes dans le cœur de Java. Vous devriez reconnaître le package «  lang   » ou encore «  nio   ». Donc, pour avoir accès aux bases fondamentales de Java il n'est plus nécessaire de charger un rt.jarpesant plus de 50 Mo mais ce module qui en fait moins de 16.
Ce découpage permet de pouvoir programmer de façon modulaire et de pouvoir fournir des fonctionnalités de manière simple. Nous définissons ce que notre module a besoin pour fonctionner (ses dépendances) dans un fichier nommé «   module-info.java  » qui est en fait une classe Java qui sera pré-compilé en  module-info.class  . Voici ce que nous pouvons trouver dans un autre module :

Fichier module-info.class
Fichier module-info.class

Dans ce fichier il y aura la liste de tout ce dont à besoin le module pour être entièrement fonctionnel, c'est le descripteur de module. Vous aurez sans doute remarqué que dans le module java.base il n'y avait pas ce fichier : c'est parce que ce module est un peu particulier car il ne dépend d'aucun autre, c'est le module parent de tous les modules (qu'ils viennent du JDK ou de vous).

Comme je vous le disais dans l'introduction, cette notion de module rajoute une portée supplémentaire à vos classes et interfaces et, pour bien comprendre ça, voici un schéma (c'est une vue schématique faite par moi pour symboliser les changements) représentant une application Java 8 et Java 9 :

Nouveau niveau de portée : me module
Nouveau niveau de portée : me module

Lorsque nous avons parlé des packages nous avons vu qu'il fallait déclarer vos classes publiques pour qu'elles soient visibles et accessibles à l'extérieur du package. Avec la notion de module, vous allez devoir explicitement dire si un package doit être accessible : vous pourrez avoir plusieurs packages dans un module mais seules les classes publiques des packages que vous rendrez disponibles (via le fichier module-info) pourront être utilisées.

En bref, les modules permettent de découper une application Java en fonctionnalité liées par où des dépendances sont spécifiées pour pouvoir fonctionner, à l'exception du module de base. De ce fait, dans les modules, en plus de notre code habituel, nous devrons dire quels modules sont nécessaires au fonctionnement de notre module mais aussi quelles sont les classes à « exporter » afin de pouvoir utiliser notre module et tout ce fait dans le fichier dont nous parlons depuis quelques paragraphes maintenant. En fait, pour faire simple, un module est un ensemble de package dont les dépendances et les classes publiques utilisables sont notées dans le fichier «  module-info.java  ».

Je vous propose maintenant de regarder comment les modules sont fait, ceci en examinant certains modules présent dans Java 9.

Inspectons des modules existants...

Pour pouvoir faire ceci, vous aurez besoin du JDK 9 et non le JRE 9 d'Oracle. Une fois installé, lancez une invite de commande et allez dans le dossier « bin » présent dans le JDK 9. Il y a des binaires intéressant et celui que nous allons utiliser se nomme « java ».
Il est possible, grâce à ce binaire, de lister et de voir les dépendances de tous les modules présents nativement dans Java.
Lancez maintenant la commande suivante :

java –list-modules

Vous devriez avoir sous les yeux la liste de tous les modules disponibles, comme le montre l'image ci-dessous.

Liste des modules Java 9
Liste des modules Java 9

Le numéro qui suit le nom du module est en fait son numéro de version actuel.
Je vous propose de voir maintenant la description de tous les modules, tapez ceci dans votre invite de commande, toujours dans le même dossier que précédemment :

FOR /F "tokens=1 delims=@" %P IN ('java --list-modules') DO (java --describe-module %P)

Là, vous avez la description de tous les modules Java 9, sans exception. Regardons-en un de plus prêt pour voir comment exporter des packages et informer des dépendances.

De mon point de vue, un module intéressant a regarder est  jdk.scripting.nashorn  , peu importe à quoi il sert, ce qui est intéressant c'est que son descripteur de déploiement est assez riche. En effet, il contient un bon nombre des mots clés pour la déclaration de modules. Voici sa description écourtée pour ne nous focaliser que sur les lignes intéressantes :

java --describe-module jdk.scripting.nashorn
jdk.scripting.nashorn@9.0.1
exports jdk.nashorn.api.scripting
...
requires java.scripting transitive
requires java.logging
provides javax.script.ScriptEngineFactory with jdk.nashorn.api.scripting.NashornScriptEngineFactory
…
qualified exports jdk.nashorn.internal.objects to jdk.scripting.nashorn.shell
…

Voici quelques mots clés expliqués :

  • export <package>  : Ceci explique que tous les types publiques de ce package pourront être utilisés dans les autres modules.

  • qualified exports <package1> to <package2>,<package3>,...  : ici nous spécifions que le package 1 est accessible au package2, et uniquement au package 2, 3 etc. Seuls les packages de la liste définie auront donc accès aux types publiques du package 1.

  • requires <package>  : par défaut, un module ne connaît pas les autres modules disponibles, ils faut donc spécifier qu'un module dépend d'un autre. Ici, le module que nous avons décrit a besoin de plusieurs autres modules pour pouvoir fonctionner. Exception faite du package de base : il n'est pas nécessaire de noter cette dépendance car tous les modules dépendent de lui, sa déclaration est donc facultative.

  • requires <package> transitive  : Ceci signifie que le module dépend du package mais que le programme qui va utiliser ce module va également en dépendre. Ceci peut arriver si, par exemple, une méthode de notre module retourne une instance d'objet qui est défini dans la dépendance : par transitivité, vous pourrez manipuler des objets présents dans la dépendance. Pour faire simple, cette déclaration n'impacte pas vraiment le module, elle impacte l'utilisateur du module en l'informant qu'il va falloir aller lire dans d'autres modules.

  • provides <package.Interface> with <package.Implem>  : cette déclaration permet de spécifier qu'une interface ou une classe abstraite fournit un service (celle spécifiée par provides) et qu'il existe une implémentation concrète décrite après le mot clé «  with  ».

Ce sont sûrement les mot clés que vous serez peut-être amené à utilisé. Cette présentation faite, nous allons maintenant pouvoir créer notre premier module, vous allez voir, c'est enfantin.

Création de votre premier module

Avant de foncer tête baissée, il faut que vous sachiez que les modules Java 9 sont soumis à des règles strictes, elles ne sont pas nombreuses mais il faut impérativement les suivre :

  • Le nom d'un module doit être unique, tout comme pour un nom de package ;

  • Chaque module ne doit contenir qu'un et un seul fichier « module-info.java » et il doit être placé à la racine du module;

  • Un module peut contenir plusieurs packages mais un package ne peut être que dans un et un seul module.

  • Le fichier module-info.java doit être à la racine de votre code source Eclipse.

Cette petite mise au point faite, créons notre module. Je vous invite donc à créer un nouveau projet Java, disons « Module1 ». Ensuite, après avoir fait un clic-droit sur votre projet, ajoutez-y un nouveau package. Ensuite, je vous invite a créer les fichiers présents sur la capture d'écran ci-dessous, au même emplacement bien entendu :

Premier module Java 9
Premier module Java 9

J'ai donc appelé mon module «   mon.module.java  » et celui-ci ne contient qu'un seul package contenant, lui, une seule classe publique : le descripteur de module est donc très simple. Afin de pouvoir l'utiliser, nous allons tout de suite créer un deuxième module, qui dépendra de celui que nous venons de faire : appelons-le «  Module2  ». Je vous invite a créer un package contenant une simple classe de test et un descripteur de module, comme ceci :

Un deuxième module utilisant le premier
Un deuxième module utilisant le premier

J'ai ajouté l'instruction qui dit que ce module est dépendant du module que nous avons fait précédemment et, comme vous pouvez le voir sur l'image ci-dessus, ma déclaration est en erreur dans Eclipse, ceci car mon module ne fait pas parti des modules qu'Eclipse connaît. Nous allons donc devoir exporter notre module et l'inclure dans notre nouveau projet. Pour faire ceci, il faut faire un fichier JAR pour Java Archive. C'est très simple à faire avec Eclipse, il vous suffit de suivre les étapes suivantes :

  • faites un clic-droit sur le projet Module1 puis choisissez «  export  » ;

  • choisissez «  Jar file  » dans la section «  Java  » ;

  • cochez tout le dossier «  Module1  »;

  • sélectionnez l'endroit où sera généré le fichier JAR et donnez-lui un nom ;

  • cliquez sur le bouton Finish.

Comme ceci :

Export d'un module
Export d'un module

À ce stade, nous avons un fichier tester.jar sur notre bureau. Il nous faut maintenant dire à notre nouveau module où se trouve le module qui lui manque tant ! Pour ce faire, faite un clic-droit sur votre deuxième projet puis allez dans le menu «  Build path > Configure build path   ». Sélectionnez «  Modulepath   » puis cliquez sur le bouton « Add External JARs... », sélectionnez le fichier que nous avons généré puis cliquez sur « Apply and close » . La figure ci-dessous montre la fenêtre et les différents boutons que nous avons utilisé.

Importer un module personnalisé
Importer un module personnalisé

Vous devriez voir que votre descripteur de module n'est plus en erreur maintenant. Nous pouvons donc tester très facilement avec ce code :

package tester.module;

import test.module.Tester;

public class ModuleTester {

	public static void main(String[] args) {
		new Tester().direBonjour("tout le monde !");
	}
	
}

Et voilà, ce chapitre sur les modules Java touche à sa fin. J'espère que vous avez apprécié.

En résumé

  • Les modules sont une part intégrante du langage.

  • Ils sont apparus avec Java 9.

  • Le langage Java lui-même est structuré en module.

  • Les modules ajoute une portée supplémentaire à vos types publiques dans vos packages car il faut spécifier explicitement si un package est accessible.

Exemple de certificat de réussite
Exemple de certificat de réussite