Let’s talk about Java 9 once more. We’ve published a blog post about our favorite Java 9 API changes. Beyond the updates in that blog post, there are many more improvements, performance fixes, and changes that went into JDK 9. But today, I’m focusing in on the Java Platform Module System, JPMS, or as us mere mortals call it “Java 9 modules”. An incredible amount of time, effort, and brainpower went into reorganizing the JDK internals, discussing the API for the module system and making sure the end result satisfies both the developers working on the JDK as well as Java developers who build applications on top of the JDK.
This post continues the cheat sheet series aiming to provide you with concise details about various technical topics. A blog post always comes with a cheat sheet that goes into more details about each topic. If you haven’t seen any of our cheat sheets, check out a few in the list below:
- Java regular expressions cheat sheet
- Java 8 Best Practices
- Java 8 Streams
- Git commands
- SQL cheat sheet
And many more. You can see all of them on our cheat sheets page.
So, let’s look at the Java Platform Module system, or Java 9 modules. While we’re eagerly waiting for the Java 10 release in March 2018, some teams probably haven’t had time yet to migrate to Java 9 and modularize their projects. If you see yourself using Java 9 now or in the future, this 1-page reference for the most important Java 9 modules concepts, keywords, and command line options could be really handy.
Download and print out this cheat sheet so you can use it whenever you need. To get fuller explanations and more detailed content about Java 9 modules than in a printable 1-page cheat sheet, continue reading this blog post!
Java Platform Module System
A long, long time ago in a galaxy right where you and I reside right now, a very smart and ambitious team decided to tackle the issue of modularizing Java. And they succeeded. Naturally, there’s a JSR that describes the Java Platform Module System, and a number of JEPs that implement the individual changes in this epic work, including: Encapsulate Most Internal APIs, Modular Run-Time Images, Module System, Modular JDK, among others. This shows the amount of work that went into this change. In this post we’ll give a short and mostly incomplete description of the things you need to know to start working with Java 9 modules.
A module is either a JAR file containing a module descriptor, or an exploded version of that. A module descriptor is a file that its access rules as it’s dependencies. There are many more details as well, but all you need to do is take a jar file, put in a
module-info.class file (compiled from a
module-info.java file), and it becomes a named module. A module establishes limits to what the components of a Java program can access within it, think of it as a special mechanism to enhance the visibility rules of the Java language, and strengthen the integrity of the platform.
Before we talk about how to manipulate and connect the modules, let’s see what kind of module types exist, and which of them are preconfigured.
- Java SE and JDK modules are the modules provided by the JDK: java.base, java.xml, etc. They export all the packages you could access previously, like java.util.
- Named application modules are your application modules that contain the module-info.class file; they have to explicitly state which modules they depend on, including what packages and services they need. They cannot read the unnamed module. (see below)
- Automatic modules are jar files on the module-path without the module-info.class file. The module name is derived from the Automatic-Module-Name MANIFEST.MF entry or the filename of the jar; they can read all modules, including the unnamed module, and are a good way to start the migration to modularity.
- Unnamed module contains all the jars and classes on the classpath; can read all modules.
module module.name– declares a module called
requires module.name– specifies that our module depends on the module
module.name, allows this module to access public types exported in the target module.
requires transitive module.name– any modules that depend on this module automatically depend on
exports pkg.namesays that our module exports public members in package
pkg.namefor every module requiring this one.
exports pkg.name to module.namethe same as above, but limits which modules can use the public members from the package
uses class.namemakes the current module a consumer for service
provides class.name with class.name.implregisters
class.name.implclass a service that provides an implementation of the
opens pkg.nameallows other modules to use reflection to access the private members of package
opens pkg.name to module.namedoes the same, but limits which modules can have reflection access to the private members in the
- Automatic-Module-Name: module.name – declares stable module name for non-modularized jar
- Add-Exports: module/package – exports the package to all unnamed modules
- Add-Opens: module/package – opens the package to all unnamed modules
(-p)specifies the module path; provide one or more directories that will contain your modules.
--add-readssrc.module=target.module – a command-line equivalent of the requires clause in a module declaration.
--add-exportssrc.module/pkg.name=target.module – a command line equivalent of the exports clause.
--add-openssrc.module/pkg.name=target.module – a command line equivalent of the open clause in a module description.
--add-modules– adds the listed modules to the default set of modules.
--list-modules– displays the names and version strings of the observable modules.
--patch-module– adds or overrides classes in a module. Replaces -Xbootclasspath/p.
--illegal-access=permit|warn|deny– relaxes strong encapsulation of the module system to show one global warning, show all warnings, or fail with errors respectively; The Java 9 default is permit.
To declare a jar file as a named module, one needs to provide a
module-info.class file, which is, naturally, compiled from a
module-info.java file. It declares the dependencies within the module system and allows the compiler and the runtime to police the boundaries/access violations between the modules in your application. Let’s look at the file syntax and the keywords you can use.
One great thing about the
module-info.java syntax is that the modern IDEs would fully support your efforts of writing them. Perhaps all of them would work beautifully, I know that IntelliJ IDEA does content assist, quick fixes of the module files when you import classes from the module you haven’t required yet, and so on. I don’t doubt Eclipse IDE and NetBeans IDE offer the same.
The Java Platform Module System is heavily about the boundaries and enforcing the visibility rules between the packages and the modules. It can be hard to grasp which modules see what, especially if you don’t have much experience with the
In the cheat sheet we have a table of the visibility rules, which was heavily inspired by the awesome blog posts about the Java 9 modules by Nicolai Parlog. Here’s what he writes about the visibility, check out the summary section of this blog post.
In this table we are able to distinguish between the compile time access and the runtime access, because you can relax what modules are allowed to see providing options on the command line when you start your java process. Read it like this, when you use a certain mechanism, the code on the left in the column like Compile time access, has the access to the members of the code in the module either public, or private through the reflection if it is specified in the table. If a module has reflective access to the private types, it has normal access to the public types too.
Important MANIFEST.MF attributes
If you’re ready to modularize your code, but are worried about the dependencies, and whether they’ll modularize their code in time, worry a bit less. Of course a modular jar won’t see the classes on the classpath and in the non-modular jars by default. So one needs a minimally intrusive way to make a jar pretend it’s modular, without complex code changes that’ll break everything. Such a way, luckily, exists. You can specify some attributes in the
MANIFEST.MF file of the jar.
With these options you can control what a normal, non-modular jar file looks like in the module system, which provides you with an easier path for migrating an existing non-modular application.
Java command line options for Java 9 modules
In addition to all the options and the keywords in the
module-info.java file, you need to know a number of command line options for the
java command, which support the module system at runtime. Here are the most essential flags:
Naturally, this isn’t a full or rigorous description of the Java Platform Module System, and I encourage you to learn more about it. There’s a number of resources that you can try for that: Nicolai’s blog, Jigsaw quickstart, State of the module system, the Module System JEP itself, and I’m sure countless blog posts all over the internet. But as a starting point, and as a quick reference, this blog post can be a valuable resource.
In this post we looked at some of the Java Platform Modules System, or for short the Java 9 modules. We briefly covered the overall concepts, module files, keywords, how providers and services work and some command line options. These are probably the most frequently used parts of the Java 9 module system a developer will need to know about, whether developing with or migrating to Java 9 modules.