Working with Java Modules (JPMS)
1. Understanding module-info.java
| Element | Purpose |
|---|---|
module name { ... } | Declare module |
| Location | Root of source tree |
| Compiled to | module-info.class |
| Java version | 9+ |
Example: module-info.java
module com.acme.app {
requires java.net.http;
requires transitive com.acme.api;
exports com.acme.app.public.api;
opens com.acme.app.dto to com.fasterxml.jackson.databind;
provides com.acme.spi.Greeter with com.acme.app.HelloGreeter;
uses com.acme.spi.Greeter;
}
2. Using requires Directive
| Form | Effect |
|---|---|
requires M | Compile + runtime dep |
requires static M | Compile-only (optional at runtime) |
requires transitive M | Re-export to consumers |
3. Using exports Directive
| Form | Effect |
|---|---|
exports p | Public API for all |
| Default | Packages are NOT exported |
| Granularity | Per-package (not per-class) |
4. Using opens Directive
| Form | Effect |
|---|---|
opens p | Allow deep reflection at runtime |
open module M | Open all packages |
| vs exports | opens enables setAccessible(true) |
| Use | Frameworks (Jackson, Hibernate, JPA) |
5. Using Qualified Exports
| Form | Effect |
|---|---|
exports p to M1, M2 | Only listed modules see it |
opens p to M1 | Reflective access only to M1 |
| Use | Friend modules / internal sharing |
6. Using requires transitive
| Effect | Detail |
|---|---|
| Implied readability | Consumers of A see B's exports |
| Use | Aggregator modules, API surface re-export |
| Example | java.sql → requires transitive java.logging |
7. Using provides and uses (ServiceLoader)
| Directive | Use |
|---|---|
uses S | Consumer declares need |
provides S with Impl | Provider supplies impl |
| Lookup | ServiceLoader.load(S.class) |
8. Creating Modular JARs
| Step | Command |
|---|---|
| Compile | javac -d out/M --module-source-path src $(find src -name "*.java") |
| Package | jar --create --file mods/M.jar -C out/M . |
| Main class | jar ... --main-class com.acme.Main |
9. Using --module-path
| Flag | Use |
|---|---|
--module-path / -p | Where to find modules |
--module / -m | Run main of module |
--add-modules | Include extra root modules |
--add-opens | Runtime open (for legacy reflection) |
--add-reads | Force read edge |
10. Working with Automatic Modules
| Aspect | Detail |
|---|---|
| Source | Plain JAR placed on module-path |
| Name | From Automatic-Module-Name manifest or filename |
| Reads | All other modules |
| Exports | All packages |
| Use | Migration bridge for non-modular libs |
11. Migrating Legacy Code to Modules
Migration Steps
- Run on classpath as unnamed module — verify behavior
- Place dependencies on module-path → automatic modules
- Add
module-info.javato your code (top-down) - Resolve split-packages and JDK internal usage
- Convert dependencies to explicit modules
| Issue | Fix |
|---|---|
| Split packages | Refactor or merge |
| sun.misc.Unsafe | Migrate to VarHandle / Foreign Memory |
| Reflection | Add --add-opens or open module |
12. Using jdeps for Module Analysis
| Command | Use |
|---|---|
jdeps lib.jar | Show dependencies |
jdeps --jdk-internals lib.jar | Detect internal API use |
jdeps --generate-module-info ./out lib.jar | Generate stub module-info |
jdeps -s lib.jar | Summary only |