Java 9 - Modules
What is the motivation behind introducing a new module system to the JDK, influencing the entire Java stack from the virtual machine, the Java compiler, linker, packing tools like
jar and others and of course the Java standard library components themselves?
The primary goals of introducing modularisation have been mentioned as follows:
- Better maintenance of large applications and libraries/frameworks due to enforcement of strong encapsulation and explicit module dependency declaration
- Security improvements
- Improved application performance as types in modules can be detected much faster based on their package
- Enable the JDK and SE to scale down for use in small computing devices and cloud deployments
The class loader is the central component loading Java classes at run-time. Once a class is loaded, it is basically known to the runtime and the code on behalf of this class can be further executed.
As experienced Java developers know, in every java application - and particularly in web applications - there is a more or less complex hierarchy of class loaders, each class loader can have either none or exactly one parent.
When a class is looked up for the first time, the current class loader will look for this class in its class path. Class loaders are normally using a top-down approach for class lookup. So starting at the bootstrap class loader, the class loader hierarchy will be processed in reverse order.
Starting with Java 9 and the introduction of modules, class loaders are enriched with a new capability: looking up modules and retrieving classes from therein. But let’s have a look at the module concept first.
The module is a completely new Java program component. Before Java 9, there were classes in packages (with corresponding folders in the file system) and those classes have been found by class loaders. Starting with Java 9, modules are introduced directly in-between packages and class loaders. It encapsulates packages and the classes therein. It is not restricted to include only classes, it may also contain other data like resources and static information.
In modules, only explicitly exported
public classes can be used from other modules. As a matter of fact, this is a semantical change to the
public visibilty class modifier.
To declare an explicit module, a module declaration needs to be specified. This is done in a file called
module-info.java. As you can see from the file extension, this is a
.java file that indeed gets compiled into a
.class file by the java compiler.
Don’t get confused by the module-name being the same as the package name.
module com.ast.app defines the modul name,
exports com.ast.app exports all
public classes found in the
com.ast.app package inside the
com.ast.app module. If we wouldn’t specify the line containing the
exports, we would not allow access to any types, even though they might be
public, to other modules.
module-info.java is located in the source file root folder:
Directly under the
src folder is a folder for the
com.ast.app module. Inside that folder, you can find the
module-info.java together with the usual package directory structure. The
javac compiler will transform the
module-info.java into a
module-info.class file containing the information from the source file. The class file might have additional information besides the source code information: IDEs/tools might decide to include custom class-file attributes (e.g. module version, title, license etc.). We will have a look how new
jar command-line parameters can do exactly that in the next section.
module-info.class is treated by
jar like a normal class file and will be packaged into the generated
*.jar file. A JAR file with a module declaration is called a modular JAR file.
A modular JAR file can be used as a regular JAR file, it is compatible with Java versions prior to Java 9. For modularizing the Java SE platform, an additional artefact format has been introduced: JMOD. Whether this format should be standardized is an open question, according to this source.
Let’s say we have configured our module like in the example above. The class loader has been extended in Java 9 to search the so-called module path whenver doing a class lookup. The module path contains a list of either directories containing modules or locations directly pointing to modules.
In contrast to the good-old class path, the module path is used to detect modules and those modules export packages. Thats an important simplification. If based on the module declarations a type of a particular package can not be found, an error will be thrown by the JVM or Java compiler.
It is now even the case with modules that the same package must not exist in more than one module in the module path. Every module name must be unique in the module path.
When running the JVM, the module path is declared via the
--module-path (or short
-p) command-line parameter:
In the example above, the
mods directory contains all the application-specific modules. The
-m command-line parameter defines the entry point to the class implementing the
main method. Before we can execute this command, we need to create the
mods directory and use
javac to compile our module:
If we use
javac to compile our module like in the example above, the result is a so-called exploded module, an unpacked module. If we wanted to have a modular JAR file instead, we needed to run:
module-version command-line parameter enriched the
module-info.class with additional meta-data about the module’s version. By the way, another interesting
jar variant is
which updates a regular JAR file to beome a module JAR file with a module declaration. However, this is not the only migration path, we have a look at various options in the next section.
As mentioned above, besides modules JARs another format has been introduced called JMOD. JDK’s JMODs are found in the JDK installation directory and are included on demand, based on the derived module tree.
As you can see in the module declaration, we have a direct dependency to
Maybe you noticed, we do not have any dependency on the Java core classes defined in JDK’s
java.base module. That is intentional as
java.base is the only dependency every module has per default.
java.base itself exports all the platform’s core packages:
So as a matter of fact, our
com.ast.app modules directly depends on
In JSR-speak, our module
com.ast.app reads module
java.logging (and implicitly
java.logging (and implicitly
java.base) is readable by
com.ast.app - that’s the so-called readability relationship.
java.logging would have a dependency on a module other than
com.ast.app would not have a readability relation with this transitive dependency per default.
Depending on the API, this might be a show blocker. Just imagine you have a public type in a module that returns a public type of another module. In our example, let’s say our
SomeClass would have a public method returning a
With our current module declaration, this would not work for modules reading our module.
In order to overcome this issue, we can state such an implied readability in the module declaration:
Once we created the module again with implied readability for the
java.logging module, we can inspect our
email@example.com with the
jdeps is a utility tool which can be used to inspect modules or search for packages within modules. For a more detailed look at its functionality, have a look at the output of
The Unnamed Module
Most of the Java code existing nowadays was of course written before the introduction of the module concept. As the old class path resolving functionality has not been dropped in Java 9, it is still possible to find classes via “the old way”. However, starting with Java 9, every class has exactly one module. For Java code prior to Java 9, a special module has been introduced to satisfy this rule: the unnamed module.
The unnamed module has a readability relationship with every other module in order for the old code to archive compatibility. Also, the unnamed module exports all of its packages. If a package is found in the unnamed module and in an explicitly named module, the package from the unnamed module is ignored. Named modules, however, can not have a dependency on the special unnamed module, it only works in the other direction, from the unnamed to the named module.
This restriction might be obvious on first sight, but let’s assume the following scenario: your MVC framework has migrated towards providing Java 9 modules only, however, your persistence framework is still around in plain-old JAR files, being handled by the unnamed module. In such a situation, your MVC framework could not reference the persistence framework anymore, because it can’t have a dependency on the unnamed module. Due to that fact, another mechanism has been introduced: automatic modules.
With the automatic modules mechanism, it is supported to place plain-old JAR files into the module-path. Those JAR files will automatically be detected as modules, the module name will be derived from the JAR file name based on these rules.
An automatic module is said to be an implicit module because it naturally does not come with a module declaration. As it is impossible for the JVM to create the exports automatically, it will export all packages per default. As it’s impossible to determine dependencies to other modules, the rule with automatic modules is that every automatic can read and is readable by every other automatic module. These rules should give a convenient way to allow for step-by-step migration of existing Java code.
Reflection creates another interesting special case for modules. Frameworks such as Spring and others rely heavily on reflection. Loading a class via the class loader and instantiating it is a very common operation.
For a framework to load and instantiate
com.ast.app.SomeClass it would either be necessary to have a readability permission on the unnamed module, which is not possible, or to have a dependency on the named module where
com.ast.app.SomeClass is defined.
In order to still support these reflection-based scenarios, the reflection API has been revised in order to assume module readability for classes taking part in reflection. Those classes must still be
public, however, there is no need to define an explicit readability in those cases.
One of the big additions to Java 9 is the new module abstraction which has deep impacts on nearly every part in the JDK/SE stack. Not only it is a big change to the JDK/SE, it’s definitly a big change for framework/library providers but also application developers. The article will have a first look into Java 9 modules, we show how modules are declared, how they can be packaged and used. We will touch a couple of more complex topics too, but in general it is supposed to be seen as a first introduction to this topic.