Contents

Modern Java in Action 7 - notes about the module system

The Module System

Chapter 14 is “The Java Module System” and this post is a note that helps to refresh my knowledge about Java Module System.

The Module System

Recommended reading: The Java Module System by Nikolai Parlog - author’s page is nipfax.dev.

Java Module System:

  • developed for 10 years as Project Jigsaw
  • compared to packages, modules give control over which classes can see which other classes at compile time
  • makes it easier for teams to work in isolation, reuse code, maintain parts of codebase
  • better information hiding

Why? - The Intentions

Visibility

Visibility modifiers: public, protected, package-level, private - don’t control visibility beteween packages: to make a class available in other package, before java 8 you had to make it public and it became accessible for all (which is not what we want)

Class Path

All classes in flat Jar are made accessible on the class path, so two jars with the same class with different versions make the classpath “polluted” - depending od the order, the class loader picks the first one. This was called “jar hell” and casued a lot of pain.

What is this?

  • module-info.java - textual form - is a module declaration
  • binary form of the declaration in module-info.class is called module descriptor

Example:

1
2
3
4
5
6
7
module expenses.readers {
    requires java.base;                         1

    exports com.example.expenses.readers;       2
    exports com.example.expenses.readers.file;  2
    exports com.example.expenses.readers.http;  2
}
  • 1 - requires declares what expense.readers module needs (java.base is required by default, added here for demonstration purposes)
  • 2 - exports declares a package name which public classess will be visible outside the module, for anyone who requires them.

Usage

In case of such structure:

1
2
3
4
5
6
7
|─ expenses.application
  |─ module-info.java
  |─ com
    |─ example
      |─ expenses
        |─ application
          |─ ExpensesApplication.java

Compilaiton and jar building of the only module:

1
2
3
4
5
javac module-info.java \
  com/example/expenses/application/ExpensesApplication.java -d target

jar cvfe expenses-application.jar \
   com.example.expenses.application.ExpensesApplication -C target

and running:

1
2
java --module-path expenses-application.jar \
     --module expenses/com.example.expenses.application.ExpensesApplication

Module clauses

module

Module is declared by module directive:

1
2
module com.kamilachyla.app {
}

requires

requires specifies that this modules depends on another module both compile and runtime; here com.kamilachyla.app depends on com.kamilachyla.ui:

1
2
3
module com.kamilachyla.app {
  requires com.kamilachyla.ui;
}

exports

exports makes public types in specific packages available for use in other modules; here ui exports widgets and generators:

1
2
3
4
5
module com.kamilachyla.ui {
  requires com.kamilachyla.core;
  exports com.kamilachyla.ui.widgets;
  exports com.kamilachyla.ui.generators;
}

requires transitive

If the module com.kamilachyla.app wants to use classes in com.kamilachyla.core, it would have to require it, unless one of the modules it already requires (here, com.kamilachyla.ui) will require core as transitive. In such case, every module that requires com.kamilachyla.ui will implicitly also require com.kamilachyla.core:

So, intead of this code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
module com.kamilachyla.app {
  requires com.kamilachyla.core;
  requires com.kamilachyla.ui;
}

module com.kamilachyla.ui {
  requires com.kamilachyla.core;
  exports com.kamilachyla.ui.widgets;
  exports com.kamilachyla.ui.generators;
}

we can write:

1
2
3
4
5
6
7
8
module com.kamilachyla.app {
  requires com.kamilachyla.ui;
}
module com.kamilachyla.ui {
  requires transitive com.kamilachyla.core;
  exports com.kamilachyla.ui.widgets;
  exports com.kamilachyla.ui.generators;
}

exports … to …

Exporting can be selective, i.e. we can define for what specific modules the exported packages will be visible:

1
2
3
4
5
module com.kamilachyla.ui {
  requires transitive com.kamilachyla.core;
  exports com.kamilachyla.ui.widgets;
  exports com.kamilachyla.ui.generators to com.kamilachyla.ui.widgetusers;
}

open, opens, opens … to …

  • open is a module qualifier used to give other modules reflective access
  • opens allows to open packages individually and
  • opens .. to .. allows to open packages to only selected modules

uses and provides

Declares services to be used by a ServiceLoader.

Notes

  • any jar on module path without module-info becomes an automatic module
  • automatic modules implicitly export all packages
  • preferred module names: reversed domain names