Java 9 Modules
By: Andrej Suschevich
Tags: Software Development, Java, Java Development, Programming, Coding

module jdk.javadoc {

requires java.xml;



requires transitive java.compiler;

requires transitive jdk.compiler;



exports jdk.javadoc.doclet;



provides java.util.spi.ToolProvider with

jdk.javadoc.internal.tool.JavadocToolProvider;



provides javax.tools.DocumentationTool with

jdk.javadoc.internal.api.JavadocTool;



provides javax.tools.Tool with

jdk.javadoc.internal.api.JavadocTool;

}

First is the module keyword, followed by the name of the jdk.javadoc package, which depends on another java.xml package and is transitively dependent on other packages.

Let’s take a closer look at the keywords:

requires specifies the modules the current module depends on;

requires transitive specifies the transitive dependency: if the module m1 is transitively dependent on the module m2 , and we have some third module mX , which depends on m1 , then the module mX will also have access to m2;

is transitively dependent on the module , and we have some third module , which depends on , then the module requires static specifies the compile-time-only dependencies;

exports specifies the packages of the module that should be accessible for other modules (not including “sub-packages”);

exports…to… allows us to restrict the access: export com.my.package.name to com.specific.package ; that is, we can open access to the package of our module only for some other(s) package(s) of another module;

to ; that is, we can open access to the package of our module only for some other(s) package(s) of another module; uses specifies the services used by the module:

uses java.sql.Driver;

In this case, we specify the interface of the service used.

provides specifies the services provided by the module:

provides javax.tools.Tool with

jdk.javadoc.internal.api.JavadocTool;

First we put the interface — javax.tools.Tool, and after with — the implementation.

A little more about services

Let’s say we have several modules connected which implement an abstract service — MyService. When assembling the application, we can decide what service implementation to use by dropping the desired service implementation modules to --module-path:

Iterable <MyService> services =

ServiceLoader.load(MyService.class);

Thus, the returned Iterator contains a list of implementations of the MyService interface. In fact, it will contain all the implementations found in the modules on --module-path.

Why services were introduced at all? They are needed to show how our code will be used. So it’s all about a semantic role. Also, modularity is about encapsulation and security, as we can make the implementation private and exclude the possibility of unauthorized reflection access.

One more option of using services is a rather simple implementation of plugins. We can implement the plugin interface for our application and connect modules to work with them.

Let’s go back to declarations

Before Java 9 it was possible to use reflection to access almost everything, and we could do whatever we want. But as already mentioned, version 9 allows us to protect our app from “illegal” reflection access.

We can allow full reflection access to the module by declaring open:

open module my.module { }

Or, we can expose specific packages with opens:

module my.module { opens com.my.coolpackage;

}

It is also possible to use opens…to, thus opening the specific packages to the specific module(s).

Module Types

Project Jigsaw classifies modules as follows:

System Modules — Java SE and JDK modules. You can find the complete list using the java --list-modules command.

— Java SE and JDK modules. You can find the complete list using the java --list-modules command. Application Modules — the modules of our app written by us, and also the dependencies (from third-party libraries) that our application uses

— the modules of our app written by us, and also the dependencies (from third-party libraries) that our application uses Automatic Modules — these are modules with open access created automatically by Java from JAR files. Let’s say we want to run our application in a modular mode, but it uses some library. In this case, we put the JAR file on --module-path and Java automatically creates a module with the name inherited from the name of the JAR file.

— these are modules with open access created automatically by Java from JAR files. Let’s say we want to run our application in a modular mode, but it uses some library. In this case, we put the JAR file on --module-path and Java automatically creates a module with the name inherited from the name of the JAR file. Unnamed Module — an unnamed module automatically generated from all JAR files loaded to --class-path. This is a catch-all module for backward compatibility with previously written Java code.

Class-path vs module-path

With modules, a new concept appeared — module-path. In fact, this is the classpath we all know, but for modules.

Starting a modular application looks like this:

In a “classic mode”, we specify options and the full path to the main class. If we want to work with modules, we also specify -m or -module parameter, which indicates that we will run modules. That is, we automatically put our application in a “modular mode”. Then, we specify the module name and the path to the main class from the module.

Also, in addition to classic -cp and --class-path parameters we are used to working with, we add new -p and --module-path parameters, which indicate the paths to the modules used in the app.

It often happens that developers do not switch to Java 9+ because they believe they will have to work with modules. Although in fact, we can run our applications without adding a parameter for modules and use only other new features of Java 9.

JAR HELL

What is Jar Hell in a nutshell?

For example, our application depends on Library X and Library Y. Moreover, both of these libraries depend on Library Z, but on different versions: X depends on version 1, and Y depends on version 2. It’s okay if version 2 is backward compatible with version 1, we’ll have no problem then. But if not, obviously, we’ll have a versions conflict, which means the same library cannot be loaded into memory by the same class loader.

How do developers get over such situations? There are standard methods that developers have been using since the very first Java, for example: exclude, or plugins for Maven, which rename the root packages of the library. Or sometimes developers are looking for different versions of Library X to find a compatible option.

In fact, the very first Jigsaw prototypes supposed the module might have a version and allowed different versions to be downloaded by different ClassLoaders. Later that idea was removed. As a result, the “silver bullet” many of us were waiting for did not work out.

But still, the developers secured us from such problems right out of the box. In Java 9, Split Packages are forbidden. These are the packages divided into several modules. That is, if we have the com.my.coolpackage package in one module, we cannot use it in another module within the same app. If we run the application with modules containing the same packages, we’ll simply crash. This small improvement eliminates the possibility of unpredictable behavior connected with Split Packages downloading.

In addition to the modules themselves, there is a Jigsaw Layers mechanism, which also helps to cope with the Jar Hell.

A Jigsaw layer can be defined as some local module system. Here it is worth noting that the Split packages mentioned above are prohibited only within one Jigsaw layer. Modules with the same packages have a place to be, but they must belong to different layers.

It looks like this:

When the application starts, a Boot layer is created, which includes platform modules loaded by Bootstrap, additional platform modules loaded by the Extension loader, and modules of our application loaded by the Application loader.

We can create our own layers anytime and “put” modules of different versions there without any problems.

Here is a detailed presentation by Nikita Lipsky to learn more about the subject: Escaping The Jar Hell With Jigsaw Layers

Conclusion

The modules from Java 9 open up new possibilities for us, while the support for libraries today is quite limited. Of course, people run Spring, Spring Boot, and so on. But most libraries have not switched to the full use of modules. Apparently, that’s why all these changes were met rather skeptically by the tech community. Modules provide us with new opportunities, but the demand issue remains open.

And finally, here is a list of useful content to learn:

Project Jigsaw

JDK Module Summary

Paul Deitel — Understanding Java 9 Modules

baeldung.com — Introduction to Project Jigsaw

Alex Buckley — Modular Development with JDK 9