Qu'est-ce que JPMS? Présentation du système de module de plateforme Java

Jusqu'à Java 9, l'élément d'organisation de code de niveau supérieur de Java était le package. À partir de Java 9 qui a changé: au-dessus du package se trouve maintenant le module. Le module rassemble les packages associés.

Le Java Platform Module System (JPMS) est une structure au niveau du code, il ne change donc pas le fait que nous empaquetons Java dans des fichiers JAR. En fin de compte, tout est toujours regroupé dans des fichiers JAR. Le système de modules ajoute un nouveau descripteur de niveau supérieur que les JAR peuvent utiliser, en incorporant le module-info.javafichier.

Les applications et les organisations à grande échelle tireront parti des modules pour mieux organiser le code. Mais tout le monde consommera des modules, car le JDK et ses classes sont désormais modularisés.

Pourquoi Java a besoin de modules

JPMS est le résultat du projet Jigsaw, qui a été entrepris avec les objectifs déclarés suivants: 

  • Aidez les développeurs à organiser plus facilement les grandes applications et bibliothèques
  • Améliorer la structure et la sécurité de la plateforme et du JDK lui-même
  • Améliorez les performances des applications
  • Mieux gérer la décomposition de la plate-forme pour les petits appareils

Il convient de noter que le JPMS est une fonctionnalité SE (Standard Edition), et affecte donc tous les aspects de Java à partir de zéro. Cela dit, le changement est conçu pour permettre à la plupart du code de fonctionner sans modification lors du passage de Java 8 à Java 9. Il y a quelques exceptions à cela, et nous les noterons plus tard dans cet aperçu.

L'idée principale derrière un module est de permettre la collecte de packages associés qui sont visibles par le module, tout en masquant les éléments aux consommateurs externes du module. En d'autres termes, un module permet un autre niveau d'encapsulation.

Chemin de classe vs chemin de module

En Java jusqu'à présent, le chemin d'accès aux classes a été la ligne de fond pour ce qui est disponible pour une application en cours d'exécution. Bien que le chemin de classe remplisse cet objectif et soit bien compris, il finit par être un grand compartiment indifférencié dans lequel toutes les dépendances sont placées.

Le chemin du module ajoute un niveau au-dessus du chemin de classe. Il sert de conteneur pour les packages et détermine quels packages sont disponibles pour l'application.

Modules dans le JDK

Le JDK lui-même est maintenant composé de modules. Commençons par regarder les écrous et les boulons de JPMS là-bas.

Si vous avez un JDK sur votre système, vous avez également la source. Si vous n'êtes pas familier avec le JDK et comment l'obtenir, jetez un œil à cet article.

Dans votre répertoire d'installation JDK se trouve un /librépertoire. À l'intérieur de ce répertoire se trouve un src.zipfichier. Décompressez-le dans un /srcrépertoire.

Regardez à l'intérieur du /srcrépertoire et accédez au /java.baserépertoire. Vous y trouverez le module-info.javafichier. Ouvrez-le.

Après les commentaires Javadoc en tête, vous trouverez une section nommée  module java.base suivie d'une série de exportslignes. Nous ne nous attarderons pas sur le format ici, car il devient assez ésotérique. Les détails peuvent être trouvés ici.

Vous pouvez voir que de nombreux packages familiers de Java, tels que java.io, sont exportés à partir du java.basemodule. C'est l'essence d'un module rassemblant les packages.

Le revers de la médaille  exportsest l' requiresinstruction. Cela permet à un module d'être requis par le module en cours de définition.

Lors de l'exécution du compilateur Java sur des modules, vous spécifiez le chemin du module de la même manière que le chemin de classe. Cela permet de résoudre les dépendances.

Créer un projet Java modulaire

Jetons un coup d'œil à la structure d'un projet Java modulé.

Nous allons créer un petit programme qui a deux modules, l'un qui fournit une dépendance et l'autre qui utilise cette dépendance et exporte une classe principale exécutable.

Créez un nouveau répertoire dans un endroit pratique sur votre système de fichiers. Appelez ça /com.javaworld.mod1. Par convention, les modules Java résident dans un répertoire qui porte le même nom que le module.

Maintenant, dans ce répertoire, créez un module-info.javafichier. À l'intérieur, ajoutez le contenu de la liste 1.

Liste 1: com.javaworld.mod1 / module-info.java

module com.javaworld.mod1 { exports com.javaworld.package1; }

Notez que le module et le package qu'il exporte sont des noms différents. Nous définissons un module qui exporte un package.

Maintenant , créez un fichier sur ce chemin, à l' intérieur du répertoire qui contient le module-info.javafichier: /com.javaworld.mod1/com/javaworld/package1. Nommez le fichier  Name.java. Mettez le contenu du Listing 2 à l'intérieur.

Listing 2: Name.java

 package com.javaworld.package1; public class Name { public String getIt() { return "Java World"; } } 

Le listing 2 deviendra une classe, un package et un module dont nous dépendons.

Créons maintenant un autre répertoire parallèle à /com.javaworld.mod1 et appelons-le /com.javaworld.mod2. Dans ce répertoire, créons une module-info.javadéfinition de module qui importe le module que nous avons déjà créé, comme dans le Listing 3.

Listing 3: com.javaworld.mod2 / module-info.java

 module com.javaworld.mod2 { requires com.javaworld.mod1; } 

Le listing 3 est assez explicite. Il définit le com.javaworld.mod2module et nécessite com.javaworld.mod1.

A l' intérieur du /com.javaworld.mod2répertoire, créez un chemin de classe comme ceci: /com.javaworld.mod2/com/javaworld/package2.

Maintenant, ajoutez un fichier à l'intérieur appelé Hello.java, avec le code fourni dans le Listing 4.

Listing 4: Hello.java

 package com.javaworld.package2; import com.javaworld.package1.Name; public class Hello { public static void main(String[] args) { Name name = new Name(); System.out.println("Hello " + name.getIt()); } } 

Dans le listing 4, nous commençons par définir le package, puis importons la com.javawolrd.package1.Nameclasse. Notez que ces éléments fonctionnent comme ils l'ont toujours fait. Les modules ont changé la façon dont les packages sont rendus disponibles au niveau de la structure des fichiers, pas au niveau du code.

Similarly, the code itself should be familiar to you. It simply creates a class and calls a method on it to create a classic “hello world” example.

Running the modular Java example

The first step is to create directories to receive the output of the compiler. Create a directory called /target at the root of the project. Inside, create a directory for each module: /target/com.javaworld.mod1 and /target/com.javaworld.mod2.

Step 2 is to compile the dependency module, outputting it to the /target directory. At the root of the project, enter the command in Listing 5. (This assumes the JDK is installed.)

Listing 5: Building Module 1

 javac -d target/com.javaworld.mod1 com.javaworld.mod1/module-info.java com.javaworld.mod1/com/javaworld/package1/Name.java 

This will cause the source to be built along with its module information.

Step 3 is to generate the dependent module. Enter the command shown in Listing 6.

Listing 6: Building Module 2

 javac --module-path target -d target/com.javaworld.mod2 com.javaworld.mod2/module-info.java com.javaworld.mod2/com/javaworld/package2/Hello.java 

Let’s take a look at Listing 6 in detail. It introduces the module-path argument to javac. This allows us to define the module path in similar fashion to the --class-path switch. In this example, we are passing in the target directory, because that is where Listing 5 outputs Module 1.

Next, Listing 6 defines (via the -d switch) the output directory for Module 2. Finally, the actual subjects of compilation are given, as the module-info.java file and class contained in Module 2.

To run, use the command shown in Listing 7.

Listing 7: Executing the module main class

 java --module-path target -m com.javaworld.mod2/com.javaworld.package2.Hello 

The --module-path switch tells Java to use /target directory as the module root, i.e., where to search for the modules. The -m switch is where we tell Java what our main class is. Notice that we preface the fully qualified class name with its module.

You will be greeted with the output Hello Java World.

Backward compatibility 

You may well be wondering how you can run Java programs written in pre-module versions in the post Java 9 world, given that the previous codebase knows nothing of the module path. The answer is that Java 9 is designed to be backwards compatible. However, the new module system is such a big change that you may run into issues, especially in large codebases.

When running a pre-9 codebase against Java 9, you may run into two kinds of errors: those that stem from your codebase, and those that stem from your dependencies.

For errors that stem from your codebase, the following command can be helpful: jdeps. This command when pointed at a class or directory will scan for what dependencies are there, and what modules those dependencies rely on.

For errors that stem from your dependencies, you can hope that the package you are depending on will have an updated Java 9 compatible build. If not you may have to search for alternatives.

One common error is this one:

How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

This is Java complaining that a class is not found, because it has migrated to a module without visibility to the consuming code. There are a couple of solutions of varying complexity and permanency, described here.

Encore une fois, si vous découvrez de telles erreurs avec une dépendance, vérifiez auprès du projet. Ils peuvent avoir une version Java 9 que vous pouvez utiliser.

JPMS est un changement assez radical et il faudra du temps pour l'adopter. Heureusement, il n'y a pas d'urgence urgente, car Java 8 est une version de support à long terme.

Cela étant dit, à long terme, les projets plus anciens devront migrer, et les nouveaux devront utiliser les modules de manière intelligente, en capitalisant, espérons-le, sur certains des avantages promis.

Cette histoire, "Qu'est-ce que JPMS? Présentation du système de module de plateforme Java" a été initialement publiée par JavaWorld.