Advanced Command-Line Options for JPMS: --module and --add-reads Explained

Illustration for Advanced Command-Line Options for JPMS: --module and --add-reads Explained
By Last updated:

A common stumbling block for developers transitioning to the Java Platform Module System (JPMS) is the assumption that module declarations in module-info.java are always enough. In practice, you may encounter runtime errors like:

  • java.lang.module.ResolutionException: Modules not found
  • java.lang.IllegalAccessError: module X does not read module Y

These issues often arise when dependencies or runtime conditions require adjustments that can’t always be solved by editing source code. This is where advanced JPMS command-line options such as --module and --add-reads come into play.

For large enterprise applications, modular microservices, or testing environments, these flags provide flexibility to override module boundaries. Think of them as temporary access passes—used when a department (module) needs to collaborate with another without changing the corporate policy (source code).


The --module Option

Purpose

Specifies the initial module to run, replacing the traditional -cp and -jar options.

Syntax

java --module <module>/<main-class>

Example

java --module com.example.app/com.example.app.Main

Here:

  • com.example.app → module name
  • com.example.app.Main → fully qualified main class

Equivalent to running a class on the classpath, but with strict module boundaries.


The --add-reads Option

Purpose

Overrides requires directives, forcing a module to read another module at runtime.

Syntax

--add-reads <module>=<target-module>

Example

java --add-reads com.example.app=java.sql      --module com.example.app/com.example.app.Main

Here:

  • com.example.app is forced to read java.sql
  • Useful when retrofitting legacy libraries into modular systems

Use Cases

  1. Running Modular Applications
java --module-path mods -m com.example.app/com.example.app.Main
  1. Temporary Fix for Missing Dependencies
java --add-reads com.example.service=ALL-UNNAMED

Allows a named module to read all classpath modules.

  1. Testing Non-Modular Libraries
java --add-reads com.example.app=legacy.lib

Bridges modular and non-modular worlds.


Pitfalls

❌ Overusing --add-reads leads to fragile systems
❌ Depending on ALL-UNNAMED weakens modular guarantees
❌ Forgetting to update module-info.java for long-term stability
❌ Mixing classpath and module path unpredictably


Best Practices

✅ Use --module for entry points in production builds
✅ Use --add-reads only as a temporary migration aid
✅ Document all runtime flags in CI/CD pipelines
✅ Prefer explicit requires in module-info.java for maintainability
✅ Test regularly without overrides to catch hidden issues


What's New in Java Versions?

  • Java 5–8 → N/A (no modules)
  • Java 9 → JPMS introduced with --module, --add-reads
  • Java 11 → Improved tooling and wider adoption
  • Java 17 → Performance and stability improvements
  • Java 21 → No significant updates across Java versions for this feature

Real-World Analogy

Using --add-reads is like temporarily granting one department access to another’s documents. It works for emergencies, but permanent policy changes (via module-info.java) are better for long-term governance.


Summary & Key Takeaways

  • --module specifies the main module and entry point
  • --add-reads overrides requires, letting one module read another
  • These flags are powerful but should be used sparingly
  • Prefer explicit module-info.java for long-term maintainability
  • Document overrides and plan for gradual removal

FAQ: Advanced JPMS Options

1. What’s the difference between classpath and module path?
Classpath loads everything, module path enforces boundaries.

2. Why do I get “module not found” errors?
Because you didn’t specify the module path or use --module correctly.

3. How is --module different from -cp or -jar?
It enforces JPMS boundaries, unlike classpath-based execution.

4. When should I use --add-reads?
For migration or temporary fixes, not for long-term design.

5. Can I use ALL-UNNAMED in --add-reads?
Yes, but it weakens modular security and should be avoided in production.

6. How does JPMS improve security over classpath?
It hides internal APIs unless explicitly exported or opened.

7. Do frameworks like Spring require --add-reads?
Sometimes, especially with reflection-heavy components.

8. Can I migrate legacy apps incrementally with these flags?
Yes, but replace flags with explicit module-info.java later.

9. Do these flags affect jlink runtimes?
Yes, you may need consistent overrides across builds.

10. Should I use jmod or jlink with these flags?
Use jlink for runtime images; avoid long-term reliance on overrides.