Java Module System, introduced in Java 9 as part of Project Jigsaw, is a significant enhancement to the Java platform. It provides a way to modularize Java applications and the JDK itself, improving scalability, maintainability, and security. Below is a comprehensive guide to understanding and using the Java Module System.
1. What is the Java Module System?
- Definition: A system for creating modular applications by encapsulating code into modules.
- Purpose:
- Strong Encapsulation: Hide internal implementation details.
- Reliable Configuration: Explicitly declare dependencies between modules.
- Scalability: Improve the scalability of large applications and the JDK.
2. Key Concepts
a. Module
- Definition: A self-contained unit of code and data with a well-defined interface.
- Module Descriptor: Defined in a
module-info.java
file.
module com.example.mymodule {
requires java.base;
requires java.sql;
exports com.example.mymodule.api;
}
b. Module Path
- Definition: A path where the module system looks for modules.
- Usage: Replace the classpath with the module path (
--module-path
).
c. Module Types
- Named Modules: Explicitly defined modules with a
module-info.java
file. - Automatic Modules: JARs without a
module-info.java
file, automatically converted to modules. - Unnamed Module: The default module for code not part of any named or automatic module.
3. Creating a Modular Application
Step 1: Define Modules
- Create a
module-info.java
file for each module.
// module-info.java for com.example.mymodule
module com.example.mymodule {
requires java.base;
requires java.sql;
exports com.example.mymodule.api;
}
Step 2: Compile Modules
- Use the
javac
command with the--module-source-path
option.
javac --module-source-path src -d out $(find src -name "*.java")
Step 3: Package Modules
- Use the
jar
command to package modules.
jar --create --file=lib/mymodule.jar -C out/com.example.mymodule .
Step 4: Run Modules
- Use the
java
command with the--module-path
and--module
options.
java --module-path lib -m com.example.mymodule/com.example.mymodule.Main
4. Module Descriptor Directives
a. requires
- Specifies a dependency on another module.
requires java.sql;
b. exports
- Exports a package to other modules.
exports com.example.mymodule.api;
c. opens
- Opens a package for reflection (e.g., for frameworks like Hibernate).
opens com.example.mymodule.internal;
d. uses
and provides
uses
: Specifies a service interface.
uses com.example.mymodule.spi.MyService;
provides
: Specifies a service implementation.
provides com.example.mymodule.spi.MyService with com.example.mymodule.MyServiceImpl;
5. Modular JDK
- JDK Modules: The JDK itself is modularized into several modules (e.g.,
java.base
,java.sql
). - Viewing JDK Modules:
java --list-modules
6. Migrating to Modules
a. Bottom-Up Migration
- Start by modularizing the lowest-level libraries and work upwards.
b. Top-Down Migration
- Start by modularizing the application and work downwards.
c. Using Automatic Modules
- Convert existing JARs to automatic modules by placing them on the module path.
7. Best Practices
- Start Small: Begin with a few modules and gradually modularize the application.
- Use Strong Encapsulation: Hide internal implementation details by not exporting unnecessary packages.
- Leverage Services: Use the
uses
andprovides
directives for loose coupling. - Test Thoroughly: Ensure compatibility and functionality after modularization.
8. Common Issues and Troubleshooting
- Module Not Found: Ensure the module is on the module path and the module name is correct.
- Package Not Exported: Ensure the package is exported in the module descriptor.
- Reflection Issues: Use
opens
to allow reflection on internal packages.
Resources
- Official Documentation: Java Module System
- Books: “Java 9 Modularity” by Sander Mak and Paul Bakker.
- Tutorials: Java Module System Tutorial