Java - How to create a JPMS Module

dimitrilc 2 Tallied Votes 120 Views Share

Introduction

In this tutorial, we are going to learn how to create a Java module by hand.

Goals

At the end of this tutorial, you would have learned:

  1. What a module-info.java file is.
  2. How to compile a module.
  3. Describing a module.

Prerequisite Knowledge

  1. Basic Java.
  2. Basic command-line interface.

Tools Required

  1. A CLI shell, such as Command Prompt for Windows, Terminal for Mac, or Bash for Linux.
  2. JDK 9+ path/environment variables already setup.

Concept Overview

Java Module is an abstraction on top of packages that allows for more fine-grained encapsulation.

In the module-info.java file that defines every module, we can restrict the module’s dependencies, the packages that it exports, services that it consumes or offers, and whether it allows reflection.

The Bank Example

In order to see the concept of modules in action, we will create an example module. For learning purposes, we will not be using an IDE at all in this tutorial.

Let us assume that we are writing software for a bank. This bank has a software engineering team responsible for security.

The module that the security team creates is responsible for:

  1. Biometrics(information).
  2. Vault(security).

To give you a rough idea, later on, we are going to create the following modules and packages:

module: com.bank.security
package: com.bank.security.biometrics
package: com.bank.security.vault

The Security Module

First we will start with creating the security module.

  1. Create a folder called example. This is where all of our modules live.

  2. Under example, create the following directories(recursively):
    a. com/bank/security/biometrics
    b. com/bank/security/vault

  3. Under com/bank/security, create a module-info.java file. This file contains the definitions for your com.bank.security module. You do not have to understand this file for now. I will explain it in the next section.

     module com.bank.security {
     }
  4. Under biometrics, create a Fingerprint.java file. The file only needs two lines, a package declaration and an empty class.

     package com.bank.security.biometrics;
     public class Fingerprint {}
  5. Under vault, create a Guard.java file. This file also does not contain anything significant.

     package com.bank.security.vault;
     public class Guard {}

Your directory tree should now look like this

.
└── example
    └── com
        └── bank
            ├── security
            │   ├── biometrics
            │   │   └── Fingerprint.java
            │   ├── module-info.java
            │   └── vault
            │       └── Guard.java

The module-info.java File

At the minimum, a module-info.java file must include the module name. The module name is declared right after the module keyword.

    module com.bank.security

There are four types of modules.

  1. System modules: modules that came with the JDK.
  2. Automatic modules: regular jar files that are added to the module path(not the same as class path)
  3. Unnamed modules: a catch-all module that includes every jar file on the class path.
  4. Application modules: modules with the module-info.java file that application developers can create.

The com.bank.security module that we just created is an example of an application module.

Compiling the Security module

To compile the security module, we would navigate back to the directory before the example directory, and use the below javac command:

    javac -d compiled example/com/bank/security/biometrics/Fingerprint.java example/com/bank/security/vault/Guard.java example/com/bank/security/module-info.java

You should now see a directory named compiled that contains compiled class files, including the module-info.class.

The compiled directory should look like the tree below.

├── compiled
│   ├── com
│   │   └── bank
│   │       └── security
│   │           ├── biometrics
│   │           │   └── Fingerprint.class
│   │           └── vault
│   │               └── Guard.class
│   └── module-info.class

The exports Directive

Let us try to apply a common module directive: exports. Modify the module-info.java file and add the exports directive.

    module com.bank.security {
        exports com.bank.security.biometrics;
    }

This exports directive allows any other module to read the biometrics package. Without it, other modules would not be able to use the biometrics package normally.

Packaging a module

Next we will package our compiled module into a jar file.

  1. Make a directory called modules. This is where we will place the packaged jar.

  2. Run the command below to package everything under the compiled directory into a jar.

     jar -cvf modules/com.bank.security.jar -C compiled/ .

If you navigate to the modules directory, you will see the com.bank.security.jar file.

Describing a module

Lastly, we will use a java command to describe our module definitions.

    java -p modules --describe-module com.bank.security

After running the above command, you will see the information below.

    exports com.bank.security.biometrics
    requires java.base mandated
    contains com.bank.security.vault

The exports line indicates the package biometrics that we have exported earlier.
The requires line indicates that our module depends on the java.base module. All Java modules implicitly require java.base.
The contains line lists a package that is not exported at all, so this package is only usable to code inside the same module, under normal circumstances.

Solution Code

The whole project zip file can be downloaded here https://github.com/dmitrilc/DaniWebModule

Summary

We have learned how to create our own custom Java module in this tutorial. I skipped the IDE because I believe that compiling by hand is the best way to learn modules.