Working with Modules (JPMS) JAVA 9+
1. Understanding Module System
| Concept | Detail |
|---|---|
| Module | Named collection of packages + dependencies |
| Strong encapsulation | Only exported packages accessible |
| Reliable config | Dependencies declared at compile/runtime |
| Module path | Replaces classpath (or coexists) |
2. Creating Module Declarations (module-info.java)
Example: module-info
module com.example.app {
requires java.sql;
requires transitive com.example.api;
requires static lombok;
exports com.example.app.service;
exports com.example.app.internal to com.example.tests;
opens com.example.app.model to jackson.databind;
uses com.example.api.PaymentProvider;
provides com.example.api.PaymentProvider with com.example.app.StripeProvider;
}
| Directive | Use |
|---|---|
requires | Dependency |
requires transitive | Re-exported to consumers |
requires static | Compile-time only |
exports | Public API |
opens | Reflective access |
uses / provides | ServiceLoader |
3. Using requires Directive
| Form | Effect |
|---|---|
requires X | Compile + runtime dependency |
requires transitive X | Consumers automatically get X |
requires static X | Compile only (e.g., annotations) |
4. Using exports Directive
| Form | Detail |
|---|---|
exports pkg | Open to all modules |
exports pkg to A, B | Qualified export |
| Affects | public types in package only |
5. Using opens Directive
| Form | Detail |
|---|---|
opens pkg | Reflective deep access at runtime |
opens pkg to X | Qualified open |
open module M {...} | Opens all packages |
6. Using provides and uses Directives
| Directive | Use |
|---|---|
uses X | Consumer of service interface X |
provides X with Y | Y implements service X |
| Discovery | ServiceLoader.load(X.class) |
7. Understanding Service Loading
Example: ServiceLoader
ServiceLoader<PaymentProvider> providers = ServiceLoader.load(PaymentProvider.class);
providers.stream()
.map(ServiceLoader.Provider::get)
.forEach(p -> p.process(payment));
| Method | Use |
|---|---|
load(cls) | Find providers |
iterator() / stream() | Iterate |
findFirst() | First provider |
8. Compiling Modular Applications
| Command | Detail |
|---|---|
javac --module-source-path src -d out --module com.x | Multi-module |
--module-path mods | Module dependencies |
9. Running Modular Applications
| Command | Detail |
|---|---|
java --module-path mods -m com.x/com.x.Main | Run module's main class |
java -p mods -m com.x | Short form |
10. Working with Automatic Modules
| Aspect | Detail |
|---|---|
| Source | Plain JAR on module path |
| Name | From manifest Automatic-Module-Name or filename |
| Exports | All packages |
| Reads | All other modules |
| Use case | Migration bridge |
11. Understanding Unnamed Module
| Aspect | Detail |
|---|---|
| Source | Code on classpath |
| Reads | All modules |
| Cannot be required | Named modules can't depend on it |
12. Migrating to Modules
| Step | Detail |
|---|---|
| 1. Bottom-up | Start with leaves of dependency graph |
| 2. Add module-info | Declare requires/exports |
| 3. Use jdeps | Analyze dependencies (jdeps --generate-module-info) |
| 4. Bridge with automatic modules | Until all deps modularized |
13. Using jlink Tool
Example: jlink
jlink --module-path $JAVA_HOME/jmods:mods \
--add-modules com.example.app \
--launcher run=com.example.app/com.example.app.Main \
--strip-debug --no-header-files --no-man-pages --compress=2 \
--output runtime
| Aspect | Detail |
|---|---|
| Output | Custom runtime image with only needed modules |
| Benefit | Smaller distribution, faster startup |
| Restriction | All deps must be explicit modules |