To create a module, the only special thing you need to do is add a module-info.java
-file to your source folder and define your module. Formally, follow these steps:
module-info.java
): Create a file named module-info.java
within the source folder. This file is where you define the module and its properties.module com.examplemy.module
: Declares the name of the module.MickeyMouse
, but it’s best practice to follow the naming you used for the package.exports com.example.mymodule;
: Exports a package (here, com.example.mymodule
) to allow other modules to access its content.requires anothermodule;
: Specifies any modules that your module depends on.module-info.java
file. Use the -d
option to specify the output directory for the compiled bytecode (we’ll be doing this in the next topic).module-info.java
file in the root directory of your module source code.module com.example.myapp { requires java.sql; exports com.example.myapp.api; }
This simple module named com.example.myapp
:
java.sql
module to function.com.example.myapp.api
package, making it available to other modules.Project Structure:
hello-modules └── lib-core └── src ├── module-info.java └── be └── multimedi └── library └── core ├── credentials │ └── HardcodedPasswords.java ├── cui │ └── LibApp.java ├── model │ ├── Author.java │ ├── Book.java │ ├── Catalogue.java │ ├── Library.java │ ├── Loan.java │ ├── Reservation.java │ └── User.java └── util └── IsbnUtils.java
.java
files to build up the project structure shown.main
-method of the LibApp
-class, add a print-statement that prints “Welcome to the Library!” to the console output.module-info.java
file, add the necessary code to give the module a name, and to export the model
and util
packages (not the cui or credentials packages).module be.multimedi.library.core { exports be.multimedi.library.core.model; exports be.multimedi.library.core.util; }
The module descriptor, defined in the module-info.java
file, is a critical component of your module. It outlines the module’s characteristics, relationships, and access control. Here’s a breakdown of the key directives within the module descriptor:
Directive | Description |
---|---|
module | Declares the name of the module. |
requires | Specifies required modules that your module depends on. The java.base module by default is always implicitly required, so we can (but don’t need to) include it. |
exports | Makes a package accessible to other modules. Any packages you do not “export” remain encapsulated (i.e. included in the module but inaccessible to other developers using the module). |
opens | Similar to exports , but allows reflection access. |
provides | Declares a service implementation. |
uses | Indicates a service used by your module. |
requires transitive | Extends required modules to dependents. |
requires
Directiverequires
directive declares your module’s dependence on other modules.module <module-name> { requires <dependency-module>; // Other directives and code }
requires
indicates that your module requires another module to be available during compilation and runtime.requires
directive must match the declared name of the required module.To illustrate requires
, we’re going to make our application multi-modular:
Example: Organising a Library System
Module | Purpose | Dependencies |
---|---|---|
be.multimedi.library.core | Core functionalities of the library | None |
be.multimedi.library.books | Book-related features |
java.sql |
be.multimedi.library.users | User management and authentication |
|
be.multimedi.library.ui | User interface components | be.multimedi.library.core , ,
|
As you can see, all our other modules depend on our core-module:
requires
be.multimedi.library.books
module as shown in the example:BookRepository
: must have a method called “fetchAllBooks
“, which uses JDBC to return a List<Book>
, which means you need to import the model Book
-class from the core module (just do a simple import statement with Book
‘s full name).BookService
: You don’t need to implement this for the sake of this demo.module-info
:service
package available to the outside world but hide the rest.java.base
, declare this module’s requirement of the java.sql
module.Module overview:
Source code project structure:
hello-modules ├── lib-core │ └── ... ├── lib-books │ └── src │ ├── module-info.java │ └── be │ └── multimedi │ └── library │ └── books │ ├── service │ │ └── BookService.java │ └── repository │ └── BookRepository.java └── ... └── ...
BookRepository
-class and attempt to use the HardcodedPasswords
-class from the be.multimedi.library.core
-module.module be.multimedi.library.books { exports be.multimedi.library.books.service; requires be.multimedi.library.core; requires java.sql; }
package be.multimedi.library.books.repository; import be.multimedi.library.core.model.Book; import java.sql.*; import java.util.List; public class BookRepository { public List<Book> fetchAllBooks() { try ( Connection conn = DriverManager.getConnection("foo", "bar", "sausages"); PreparedStatement ps = conn.prepareStatement("SELECT * FROM book"); ) { // Code to populate list of books and return it return null; } catch (SQLException e) { System.out.println("Failed to fetch all books."); throw new RuntimeException(e); } } }
public class BookRepository { HardcodedPasswords passwords = new HardcodedPasswords(); // ❌
requires transitive
Directiverequires
we define our module’s dependencies explicitly.requires transitive
directive which can simplify our module’s definition by removing redundant dependency declarations.ui
relies on books
and users
and core
.books
and users
already rely on core
.requires transitive
:requires transitive
directive extends the reach of dependencies to dependent modules.requires transitive
, it exposes that dependency to any module that depends on it.These three types help facilitate a smoother transition from classpath-based organisation to the module-path-based one, enhancing backwards compatibility while encouraging migration.
These are modules with a module-info.java
file that explicitly declares the module’s dependencies, exported packages, and other configuration details. They are fully-fledged modules that participate in the module system and are therefore migrated.
These modules are JAR files placed on the module path that do not have a module-info.java
. Their name is derived from the JAR file name, and they automatically require
all other modules. They can access all other automatic modules and named modules but export all their packages, offering a bridge between named modules and the classpath.
Any JARs or classes placed on the classpath (not the module path) are part of the unnamed module. This module can require
any automatic modules and other unnamed modules but is not visible to named modules. It is essentially the classpath as a module.
jdeps
.jdeps
can identify dependencies on internal JDK APIs, which is vital since these APIs may not be accessible in JPMS due to encapsulation.module-info.java
Recommendations: For projects migrating to JPMS, jdeps
can suggest a module-info.java
content based on the current usage of Java APIs and other library dependencies.The Java Platform Module System (JPMS) introduces significant advances in Java’s ability to manage dependencies and improve application security through encapsulation. However, its adoption is limited in several key frameworks even as of JDK 21. Here’s a quick overview of the JPMS adoption landscape:
jlink
.module-info.java
in the source folder to define module names, exported packages, and required modules.requires
: Specifies dependencies on other modules.exports
: Allows other modules access to specified packages.requires transitive
: Extends dependencies to consuming modules, reducing redundancy.jlink
for optimized runtime images.