Java 9 Module System

Java 9 Module System, which is developed under Project Jigsaw, comes to us with the specific goal: to provide reliable configuration and strong flexible encapsulation. That helps application developers, library developers, or Java SE Platform implementors more easilier create a scalable platform, make greater platform integrity, and improve performance. In this tutorial, we’re gonna take a look at Java 9 Module System in a summarized way.

I. Why Jigsaw?

The primary goals of Project Jigsaw are to:

  • Make the Java SE Platform, and the JDK, more easily scalable down to small computing devices;
  • Improve the security and maintainability of Java SE Platform Implementations in general, and the JDK in particular;
  • Enable improved application performance;
  • Make it easier for developers to construct and maintain libraries and large applications, for both the Java SE and EE Platforms.

A standard module system was designed and implemented to have ability to modularize the JDK and other large legacy code bases.

II. Module System

1. What is Module?
1.1 Definition

A module is a named, self-describing collection of:
– code: packages containing types (Java classes, interfaces…)
– data: resources and other kinds of static information.

In summary:
– Class contains fields, methods.
– Package contains Classes, Enums, Interfaces, configuration files…
– Module contains Package and Other data resources.

1.2 Declaration

This is the way we declare a module:

module [module name] {…} defines name of a module.
requires + [module name] indicates the module that current module depends upon, at both compile time and run time.
exports + [package name] exports package to outside or to other modules. All other packages (which are not exported) are hidden.

*Note: A module can contain no requires or no exports.

The declaration code above is placed in module-info.java file which locates in the root of the module’s source-file hierarchy. For example, com.foo.bar folder module might include:

1.3 Module Artifact

A module declaration is compiled into module-info.class file, placed similarly in the class-file output directory.

A modular JAR file is like an ordinary JAR file but also includes a module-info.class file. For example, com.foo.bar folder modular JAR file might include:

Modular JAR file allows to ship a single artifact that works:
– as a module, on Java 9 and later, or
– as a regular JAR file on the class path, on all releases.

Then, a module artifact could be a modular JAR file or a JMOD file containing a compiled module definition. We can find example of JMOD files in JDK/jmods folder:

module-system

1.4 Summary

A module can be defined as:
– an exploded-module directory which name is the module’s name and content is an “exploded” directory tree corresponding to a package hierarchy, or
– a module artifact (modular JAR file or JMOD file).

2. Accessibility and Readability
2.1 Readability

Assume that we declare modules like this:

– Code in myapp module can refer to types in com.foo.bar module.
– Code in com.foo.bar module can refer to types in java.logging module.
=> So we can say that myapp reads com.foo.bar and com.foo.bar reads java.logging.
module-system-readability
For example:
– in myapp module, we can use Foo class of com.foo.bar package, and
– in com.foo.bar module, we can use Logger class of java.util.logging package.

This configuration helps things faster. It’s because code in myapp module refers to a type in a package which defined either in myapp module or in one of the modules read by myapp module (in this case, com.foo.bar module).
=> No need to search in multiple modules or entire class path.

What if, in myapp module, we want to use Logger class of java.util.logging package? Should we add a line in module declaration code like this:

It doesn’t look good, myapp have to read java.logging when:
myapp reads com.foo.bar,
com.foo.bar refers to types in java.logging.
=> myapp module should also have ability to logically read some other modules inside the module it reads. So we have:

Implied Readability

The declaration of module can be extended to give chance for any module (that depends upon it) to have readability to additional modules. Implied Readability is expressed by adding public modifier, like this:

module-system-implied-readability

2.2 Accessibility

Before Java 9, the accessibility can be described with:
– public
– protected
– [package]
– private

In this scenario, the only way to share code between packages is using public. But, it means ‘available to everyone’.

One goal of module system is strong (but flexible) encapsulation that allows a component to declare which public types are accessible by other components and which are not.

So now, levels of accessibility are:
– public to everyone
– public but only to specific modules
– public only within a module

– protected
– [package]
– private

public type in a package is no longer accessible outside the module, by default.

Return to the example above:

com.foo.bar.alpha is public to everyone (who reads com.foo.bar module).
com.foo.bar module contains com.foo.bar.beta package, but the package is inaccessible because it is not exported, although components inside the package have been declared with public modifier. So com.foo.bar.beta is public only within itself.
– We can allow a package to be exported to one or more specified modules, and not exported to others by:
export [package] to [module, module,…];

So, if type A is defined in module a, and type B is defined in module b.
Code in A can access B if:
a reads b, and
– package that includes B (in module b) treats module a by ‘public‘ way (public to everyone/ public to set of modules that includes module a).

Because basing on static information in module-info.java files, Readability and Accessibility work at compile time, without regards to class loaders.
3. Kinds of Module
3.1 Named Module

All modules in the examples above are named module.
So, named module is the module explicitly declares with a name in module-info.java, then is loaded from the module path.

What about JAR file in class path that doesn’t have module-info.java file?
Most Java code was written before release of module system and should be able to work without change. Even though module system platform is combination of modules, it must compile and run well with JAR files in the class path.

Here we go to next kind of module.

3.2 Unnamed Module

Unnamed module, akin to concept of unnamed package, is the module which packages or classes are not defined in any named module, but exists in JAR file from class path. If our code tries to load a Type from those files, module system attempts to look up class path and load it.

Unnamed module can:
– read all other modules (including all of the named, built-in platform modules).
– exports all of its packages.

The package in the unnamed module will be ignored if it is also defined in named module.

For example, class path’s JAR file containing com/foo/bar/alpha/AlphaFactory.class will never be loaded, because com.foo.bar module exports com.foo.bar.alpha package.

To keep module system to be reliable dependencies, named module cannot read unnamed module so that named module doesn’t depend on arbitrary content of class path.

In module-info.java, we CANNOT write:

It’s uncomfortable if we want to use a JAR outside set of named modules, we have to turn it into a named module, then new module cannot access the JAR files left in class path.

Let’s come to next kind.

3.3 Automatic Module

The JAR that we desire to use in previous part can be turned into automatic module, just by moving it from the class path to the module path.

An automatic module is a named module that is defined implicitly without a module-info.java or module declaration. Its name is derived from the JAR file name. We can associate this kind of module with implicit module or automatically named module.

Automatic module can:
– read any module (including other automatic modules and unnamed modules).
– export all of its package.

3.4 Summary

Three kinds of module can be illustrated in one image below:
module-system-kinds-of-module

4. Services

Java has a java.util.ServiceLoader class that helps to locate service providers at runtime by searching in class path.

For service providers defined in modules, we can look at an example to know way to declare modules with service and how it works.
Assume that we have demo.app module that we want to use Logger that is retrieved from System.getLogger() factory method with help of LoggerFinder service.

For both efficiency and clarity, we can add a uses clause. If not, module system is still able to identify by scanning the class files in module artifacts, but it would be slow and unreliable.

This is demo.app.MainApp class:

This is LoggerFinder implementation inside demo.logging module:

In demo.logging module declaration, we provides an implementation of LoggerFinder service with a provides – with clause:

Using provides and uses clause gives benefit:
– The declarations of provision and use can be interpreted at compile time to ensure that service interface java.lang.System.LoggerFinder is accessible to both demo.logging module and demo.app module.
demo.logging module declaration can be further interpreted to ensure that MyLoggerFinder actually implements the declared service interface java.lang.System.LoggerFinder.

For detailed example with step by step and result to check, please visit:
Java 9 Platform Logging API and Service


Related Posts



1 thought on “Java 9 Module System”

Got Something To Say:

Your email address will not be published. Required fields are marked *

*