Modules system was added to Java in version 9, but I've never used it before. I think I never need it. I'm using Maven for my work and home Java projects and I never thought modules will bring something worth the effort of learning of new technology. Recently I was doing small Java application for my friends. It was good opportunity for learning modules for fun and profit.
One of the features of Java modules is strong encapsulation.
It means that example package com.acme.example
from Module A
is accessbile in Module B
when all following conditions are met:
Module A
explicitly exports package com.acme.example
Module B
explicitly declares, that requres
package com.acme.example
.Each module declares exported and required packages in special file module-info.java
,
that is saved in top-level directory. In my Maven project it was src/main/java
.
My project is a Java Swing application, that depends on Apache POI
library. In that case module-info.java
looks like this
module com.milosz.mergeupp {
requires java.desktop;
requires java.logging;
requires org.apache.poi.poi;
}
I declared, that my application requires java.desktop
and java.logging
modules from
Java SDK.
Without that statement I couldn't use Swing classes or Java Logging config. Modern IDE
are supporting moduels and immediately inform about missing dependencies. Last line
org.apache.poi.poi
declares dependency on Apache POI package. java.base
module is
provided by default and don't have to be included in require list.
Unlike in Maven if I declare dependency on Module A
doesn't mean, that I also have
access to transitive dependencies of that module.
For example I won't be able use classes from library commons-collections4
, that is
dependency of Apache POI until I add org.apache.commons.collections4
to requirements list
in module-info.java
.
In order to build modularized application with Maven, ensure that you're using
maven-compiler-plugin
at least in version 3.6.0
.
Application is quite small and takes about 18 MB with all dependencies. At the same time user needs to download ~200 MB package with Java runtime. Of course same runtime can be reused by all Java applications, but still 200 MB doesn't look good, when compared application size.
Since I've already implemented modules I've decided to try jlink
- Java linker, that was
developed in Project Jigsaw.
jlink
can assemble and optimize a set of modules and their dependencies into a custom
run-time image.
If I used it for com.milosz.mergeupp
modules, runtime would include only those modules,
that are required for running application.
Unfortunatelly it didn't work as I expected.
Maven maven-jlink-plugin
supports building custom runtime for module project.
It has failed on first attempt, because jlink
doesn't support so called automatic modules.
It means, that modules without explicit modules-info.java
cannot be used with jlink
.
In my case that was all apache-commons
packages, which are dependencies of Apache POI.
I've workaround that limitation with moditect-maven-plugin
.
This Maven plugin is able add custom module-info.java to any project dependency.
Example configuration for commons.collections4
module might look like this
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
<version>1.0.0.RC2</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>add-module-info</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/modules</outputDirectory>
<modules>
<module>
<artifact>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</artifact>
<moduleInfoSource>
<![CDATA[module commons.collections4 {
exports org.apache.commons.collections4.multimap;
} ]]>
</moduleInfoSource>
</module>
</configuration>
</execution>
</executions>
</plugin>
Because POI library requires access to org.apache.commons.collections4.multimap
module,
it must be exported in dependency.
Overriden pacakges land in /target/modules
directory and must be explicitly added in
jlink
plugin configuration.
After I did it for all automatic modules I could build custom runtime. However package took over 200 MB.
I really like Java modules concept and features. Especially explicit list of modules, that
can be used in code. I've never liked ability to use transitive dependencies in Maven
projects. Requirements list in modules-info
ensures, that we don't couple our application
with random library.
However I don't think I will use jlink
again. It basically required too much configuration.
And output wasn't encouraging. Huge runtime, that can be run only on host system (you can't
build Windows runtime on Linux). Missing modules-info
in popular Apache Commons libraries
was also unpleasant surprise.