Java Platform Module System (JPMS) is Java’s new modularity to provide a higher-level aggregation above packages. It has been introduced as part of Java 9 as an outcome of Project Jigsaw.
The primary goals are to
- Make it easier for developers to construct and maintain libraries and large applications
- Improve the structure and security of the platform and JDK itself
- Improve the performance
- Better handle decomposition of the platform for smaller devices1
JVM before version 9 is monolithic, which can cause slow start-up times and more RAM. Java 9 also changed the internal structure of JDK to use modularity there. So, the core java libraries are split into lots of small modules following the single responsibility principle, which allows creating lightweight runtime when required.
What Is a Module?
A Java module is a group of closely related packages and resources along with a new module descriptor file2. It is packaged as a modular jar file. The module also specifies which packages to be visible to other Java, and which modules to depend on.
Some of the key points to be aware of:
- A module is a set of packages designed for reuse3.
- Changed the meaning of public: public to everyone, public but only to friend module or public only within a module.
- A modules export packages (i.e its own packages) to make available but requires modules (i.e java.base) as a dependency
- Modules enable exposing some packages but also hiding some. The user of the modular jar benefits from an explicit coherent API ( packages in “exports” of module-info.java), the releaser benefits from the concealed packages inside the module which support the exported API. Even if there is high coupling inside the module, there is loose coupling between the modules.
- The release package is still a jar ( but called modular jar) also containing module-info.class
- JDK 9 looks for modules in the system image (i.e JDK installation directory) and on the module path (i.e directories containing the modular jars, not directly jars themselves)
- Module dependencies are checked when running so no runtime dependency error.
- Modules take the strain of encapsulation and concealment so the packages can focus solely on being namespaces3.
- Modules make packages as cheap as classes and then control the reuse of the packages very precisely
Migrating to Modules
Considering the transitioning to the modules will not be at once, a hybrid option is needed. Java 9 has support for the combination of modular (in modular jars) and non-modular code ( traditional jars). Java Module System creates automatic modules for a non-modular jar.
An automatic module is a module whose declaration is inferred by the modules system from a jar on the module path. Automatic modules basically require everything, it emulates the class path.
It is advised not to assume older versions of tools and libraries will run on JDK 9. So, it is better to check with its documentation or ask the maintainers4.
If you are a library maintainer, even if you don’t migrate it for a while, it is advised to set a module name in the jar manifest.
Services in Modules
Java 9 also introduced a new concept, service, in module system to enable plugging an implementation not known at compile time. It can be a confusing name for some, especially the ones who use Spring but it has nothing to do with dependency injection.
A service is an interface or abstract class and it is located in a service interface Java module. A service provider is an implementation of a service and it is provided by separate Java modules other than the interface module. And Java’s service loader is a facility to load service providers that implement a service interface.
A Java module or application can require the service interface module and code against the service interface, without knowing exactly which other module delivers the service implementation. The implementation is discovered at runtime and depends on what service implementation modules are available on the Java module path when the application is launched5.
So services enable handling dependencies on compile-time and enable providing the implementation in runtime. They help to have not only loose coupling but also better separation of concerns.
What About The Project Structure?
Java still supports the structure used before Java 9. The structure with modules is very similar to what we do have with multi module projects before but with additional module-info.java file to define the module’s exposed packages or dependencies. A sample project can be found for Gradle using Kotlin and Groovy6. The sample structure looks like as below:
I have also seen some having modules under the same src folder, and then packages under them but personally, I found them a bit ugly.
Project Jigsaw: http://openjdk.java.net/projects/jigsaw/ ↩︎
A Guide to Java 9 Modularity https://www.baeldung.com/java-9-modularity ↩︎
Modular Development with JDK 9: https://www.youtube.com/watch?v=gtcTftvj0d0 ↩︎
Modules and Services: https://www.youtube.com/watch?v=RjVjm4uuMvc ↩︎
Java Modules http://tutorials.jenkov.com/java/modules.html ↩︎
Building Java Modules Sample https://docs.gradle.org/current/samples/sample_java_modules_multi_project.html ↩︎