GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||
GeezerVison is showing jenkov.com — Java Modules Java Language
Java Modules
A Java module is a packaging mechanism that enables you to package a Java application or Java API as a separate Java module. A Java module is packaged as a modular JAR file. A Java module can specify which of the Java packages it contains that should be visible to other Java modules which uses this module. A Java module must also specify which other Java modules is requires to do its job. This will be explained in more detail later in this Java modules tutorial. Java modules is a new feature in Java 9 via the Java Platform Module System (JPMS). The Java Platform Module System is also sometimes referred to as Java Jigsaw or Project Jigsaw depending on where you read. Jigsaw was the internally used project name during development. Later Jigsaw changed name to Java Platform Module System. Java Module BenefitsThe Java Platform Module System brings several benefits to us Java developers. I will list the biggest benefits below. Smaller Application Distributables via the Modular Java PlatformAs part of Project Jigsaw, all the Java Platform APIs have been split up into separate modules. The benefit of splitting all the Java APIs up into modules is that you can now specify what modules of the Java platform your application requires. Knowing what Java Platform modules your application requires, Java can package up your application including only the Java Platform modules that your application actually uses. Before Java 9 and the Java Platform Module System you would have had to package all of the Java Platform APIs with your Java application because there was no official way of reliably checking what classes your Java application used. Since the Java Platform APIs have grown quite large over the years, your application would get a large amount of Java classes included in its distribution, many of which your application would probably not be using. The unused classes makes your application distributable bigger than it needs to be. This can be a problem on small devices like mobile phones, Raspberry Pis etc. With the Java Platform Module System you can now package your application with only the modules of the Java Platform APIs that your application is actuallly using. This will result in smaller application distributables. Encapsulation of Internal PackagesA Java module must explicitly tell which Java packages inside the module are to be exported (visible) to other Java modules using the module. A Java module can contain Java packages which are not exported. Classes in unexported packages cannot be used by other Java modules. Such packages can only be used internally in the Java module that contains them. Packages that are not exported are also referred to as hidden packages, or encapsulated packages. Startup Detection of Missing ModulesFrom Java 9 and forward, Java applications must be packaged as Java modules too. Therefore an application module specifies what other modules (Java API modules or third party modules) it uses. Therefore the Java VM can check the whole module dependency graph from the application module and forward, when the Java VM starts up. If any required modules are not found at startup, the Java VM reports the missing module and shuts down. Before Java 9 missing classes (e.g. from a missing JAR file) would not be detected until the application actually tried to use the missing class. This would happen sometime at runtime - depending on when the application tried to use the missing class. Having missing modules reported at application startup time is a big advantage compared to at runtime when trying to use the missing module / JAR / class. Java Module BasicsNow you know what a Java module is and what the benefits of Java modules are, let us take a look at the basics of Java modules. Modules Contain One or More PackagesA Java module is one or more Java packages that belong together. A module could be either a full Java application, a Java Platform API, or a third party API. Module NamingA Java module must be given a unique name. For instance, a valid module name could be com.jenkov.mymodule
A Java module name follows the same naming rules as Java packages. However, you should not use underscores
( It is recommended to name a Java module the same as the name of the root Java package contained in the module - if that is possible (some modules might contain multiple root packages). Module Root Directory
Before Java 9 all Java classes for an application or API were nested directly inside a root class directory
(which was added to the classpath), or directly inside a JAR file. For instance, the directory structure for
the compiled packages of com/jenkov/mymodule A little more graphically, it would look like this:
From Java 9 the Java Platform Module System offers an alternative directory structure which can make it easier
to compile Java sources. From Java 9 a module can be nested under a root directory with the same name as the module. In the example
above we have a directory structure for a package named The directory structure for the above Java package contained in a Java module of the same name, would look like this: com.jenkov.mymodule/com/jenkov/mymodule A little more graphically, it would look like this:
Notice the fullstops (
The module root directory is used both for the source files and compiled classes of a Java module. That means,
that if your Java project has a source root directory named src/main/java/com.jenkov.module1 src/main/java/com.jenkov.module2 The same directory structure would be seen in the Java compiler's output directory. It is common to only have one Java module per project. You will still need the module root directory in that case, but the source and compiler output root directories will only contain a single module root directory. In the section Compiling a Java Module I explain how the above module root directory structure can make it easier to compile all Java sources for a Java module. Module Descriptor (module-info.java)
Each Java module needs a Java module descriptor named The module descriptor specifies which packages a module exports, and what other modules the module requires. These details will be explained in the following sections. Here is how a basic, empty Java module descriptor looks: module com.jenkov.mymodule {
}
First is the
Notice also how the module descriptor is suffixed Module ExportsA Java module must explicitly export all packages in the module that are to be accessible for other modules using the module. The exported packages are declared in the module descriptor. Here is how a simple export declaration looks inside a module descriptor: module com.jenkov.mymodule {
exports com.jenkov.mymodule;
}
This example exports the package called
Please note, that only the listed package itself is exported. No "subpackages" of the exported package are exported.
That means, that if the To export a subpackage also, you must declare it explicitly in the module descriptor, like this: module com.jenkov.mymodule {
exports com.jenkov.mymodule;
exports com.jenkov.mymodule.util;
}
You do not have to export the parent package in order to export a subpackage. The following module descriptor exports statement is perfectly valid: module com.jenkov.mymodule {
exports com.jenkov.mymodule.util;
}
This example only exports the Module RequiresIf a Java module requires another module to do its work, that other module must be specified in the module descriptor too. Here is an example of a Java module requires declaration: module com.jenkov.mymodule {
requires javafx.graphics;
}
This example module descriptor declares that it requires the standard Java module named Circular Dependencies Not AllowedIt is not allowed to have circular dependencies between modules. In other words, If module A requires module B, then module B cannot also require module A. The module dependency graph must be an acyclic graph. Split Packages Not AllowedThe same Java package can only be exported by a single Java module at runtime. In other words, you cannot have two (or more) modules that export the same package in use at the same time. The Java VM will complain at startup if you do. Having two modules export the same package is also sometimes referred to as a split package. By split package is meant that the total content (classes) of the package is split between multiple modules. This is not allowed. Compiling a Java Module
In order to compile a Java module you need to use the javac -d out --module-source-path src/main/java --module com.jenkov.mymodule
Remember you must have the "C:\Program Files\Java\jdk-9.0.4\bin\javac" -d out --module-source-path src/main/java --module com.jenkov.mymodule It is also possible to compile a Java module with Ant. You can see that in my tutorial about how to Build Java Modules With Ant.
When
The
The ... --module com.jenkov.mymodule1,com.jenkov.mymodule2 Running a Java Module
In order to run the main class of a Java module you use the java --module-path out --module com.jenkov.mymodule/com.jenkov.mymodule.Main
The
The Building a Java Module JAR File
You can package a Java module inside a standard JAR file. You do so with the standard
Here is the jar -c --file=out-jar/com-jenkov-mymodule.jar -C out/com.jenkov.mymodule .
The
The
The It is also possible to package a Java module with Ant. You can see that in my tutorial about how to Build Java Modules With Ant. Setting the JAR Main Class
You can still set the JAR main class when generating the module JAR file. You do so by providing a
jar -c --file=out-jar/com-jenkov-mymodule.jar --main-class=com.jenkov.mymodule.Main -C out/com.jenkov.mymodule . You can now run the main class of this JAR file with a shortcut. This shortcut will be explained in the next section. Running a Java Module From a JAROnce you have packaged your Java module into a JAR file, you can run it just like running a normal module. Just include the module JAR file on the module path. Here is how you run the main class from a Java module JAR file: java --module-path out-jar -m com.jenkov.mymodule/com.jenkov.mymodule.Main
For this command to work the module JAR file must be located in the Running a Java Module From a JAR With a Main Class SetIf the Java module JAR file has a main class set (see a few sections earlier in this tutorial for how to do that), you can run the Java module main class with a little shorter command line. Here is an example of running a Java module from a JAR file with a main class set: java -jar out-jar/com-jenkov-javafx.jar
Notice how no Packing a Java Module as a Standalone ApplicationYou can package a Java module along with all required modules (recursively) and the Java Runtime Environment into a standalone application. The user of such a standalone application does not need to have Java pre-installed to run the application, as the application comes with Java included. That is, as much of the Java platform as the application actually uses.
You package a Java module into a standalone application using the jlink --module-path "out;C:\Program Files\Java\jdk-9.0.4\jmods" --add-modules com.jenkov.mymodule --output out-standalone
The
The
The Running the Standalone ApplicationOnce packaged, you can run the standalone Java application by opening a console (or terminal), change directory into the standalone application directory, and execute this command: bin\java --module com.jenkov.mymodule/com.jenkov.mymodule.Main
The standalone Java application contains a
The Unnamed ModuleFrom Java 9 and forward, all Java classes must be located in a module for the Java VM to use them. But what do you do with older Java libraries where you just have the compiled classes, or a JAR file?
In Java 9 you can still use the The unnamed module exports all its packages. However, the classes in the unnamed module are only readable by other classes in the unnamed module - or from automatic modules (see next section). No named module can read the classes of the unnamed module. If a package is exported by a named module, but also found in the unnamed module, the package from the named module will be used. All classes in the unnamed module requires all modules found on the module path. That way, all classes in the unnamed module can read all classes exported by all the Java modules found on the module path. Automatic ModulesWhat if you are modularizing your own code, but your code uses a third party library which is not yet modularized? While you can include the third party library on the classpath and thus include it in the unnamed module, your own named modules cannot use it, because named modules cannot read classes from the unnamed module. The solution is called automatic modules. An automatic module is made from a JAR file with Java classes that are not modularized, meaning the JAR file has no module descriptor. This is the case with JAR files developed with Java 8 or earlier. When you place an ordinary JAR file on the module path (not the classpath) the Java VM will convert it to an automatic module at runtime. An automatic module requires all named modules on the module path. In other words, it can read all packages exported by all named modules in the module path. If your application contains multiple automatic modules, each automatic module can read the classes of all other automatic modules. An automatic module can read classes in the unnamed module. This is different from explicitly named modules (real Java modules) which cannot read classes in the unnamed module. An automatic module exports all its packages, so all named modules on the module path can use the classes of an automatic module. Named modules still have to explicitly require the automatic module though. The rule about not allowing split packages also counts for automatic modules. If multiple JAR files contain (and thus exports) the same Java package, then only one of these JAR files can be used as an automatic module.
An automatic module is a named module. The name of an automatic module is derived from the name of the JAR file.
If the name of the JAR file is
If a JAR file contains versioning in its file name, e.g. ServicesWith Java 9 comes a new concept called services. Java services is related to the Java Platform Module System, so I will explain Java services in this Java module tutorial. A service consists of two major parts:
The service interface is typically located in a service interface Java module which only contains the service interface, plus any classes and interfaces related to the service interface. The service implementations are provided by separate Java modules - not the service interface module. Typically a service implementation Java module will contain a single service implementation. 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 service implementation is discovered at runtime, and depends on what service implementation modules are available on the Java module path when the application is launched. Service Interface ModuleJava service interface modules do not require a special declaration of the service interface. You just create a regular Java module. Here is a Java service module descriptor example: module com.jenkov.myservice {
exports com.jenkov.myservice
}
Notice how the actual service interface is not mentioned. The service interface module only exports the Java package that contains the service interface. The service interface is just a normal Java interface, so I have not shown an example of it. Service Implementation ModuleA Java module that wants to implement a service interface from a service interface module must:
Imagine that the module com.blabla.myservice {
requires com.jenkov.myservice;
provides com.jenkov.myservice.MyService with
com.blabla.myservice.MyServiceImpl
}
The module descriptor first declares that it requires the service interface module.
Second, the module descriptor declares that it provides an implementation for the service interface
Now that this module declares that it implements the service interface, we need to see how a Java module can lookup an implementation of the service interface at runtime. Service Client ModuleOnce you have both a service interface module and a service implementation module, you can create a client module that uses the service. Sometimes a service client module is referred to as a service consumer module or service user module, but the meaning is the same - a module that uses a service specified in an external module and implemented by yet another external module. In order to use the service, the client module must declare in its module descriptor that it uses the service. Here is how to declare the use of a service in a module descriptor: module com.client.myservicelient {
requires com.jenkov.myservice;
uses com.jenkov.myservice.MyService;
}
Notice how the client module descriptor also declares that it requires the The advantage of not having to declare the service implementation modules is, that the implementation modules can be exchanged without breaking the client code. You can decide what service implementation to use when assembling the application - by dropping the desired service implementation module(s) onto the module path. The client module, and service interface module, are thus decoupled from the service implementation modules. Now the service client module can lookup a service interface implementation at runtime like this: Iterable<MyService> services =
ServiceLoader.load(MyService.class);
The returned Module VersioningThe Java Platform Module System does not support versioning of Java modules. You cannot have multiple versions of a module available on the module path when running a Java 9+ application. You will need to use a build tool like Maven or Gradle to handle versioning of your module, and the external modules your module depends on (uses / requires). Multi Java Version Module JAR FilesFrom Java 9 it is possible to create JAR files for Java modules which contains code compiled specifically for different versions of Java. That means, that you can create a JAR file for your module that contains code compiled for Java 8, Java 9, Java 10 etc. - all within the same JAR file. Here is how the structure looks of a multi Java version JAR file:
The
The Multi-Release: true
The Migrating to Java 9Parts of the Java Platform Module System are designed to ease migration of applications written in pre Java 9 to Java 9. The module system is designed to support both "bottom up" and "top down" migration. Bottom up migration means that you migrate your small utility libraries first, and your main applications last. Top down migration means that you migrate your application first, and the utility libraries later on. The migration can go something like this:
By following this migration process the chance is higher of your applications being able to work in the period before everything is upgraded to Java modules. Your application is supposed to be able to work during any of the above phases. Upgrading utility libraries first to automatic modules, and later to full modules, starting at the bottom of the dependency hierarchy should assure that your libraries can still read each other during upgrade, plus be readable by the main applications on the classpath in the unnamed module or as an automatic or named module. Note: If there is any chance of you upgrading everything in one big upgrade, that will most likely be the easiest upgrade path. Of course you will have some time where you may not be able to assemble a working application, but once you are through it, you are through it! You are not dragging old dependencies around in your application. If possible, this is definitely preferable. Next: Java Method Chaining
Close TOC
All Trails Trail TOC Page TOC Previous Next | ||||||
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. | ||||||