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 namecom.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 readjava.sql
- Useful when retrofitting legacy libraries into modular systems
Use Cases
- Running Modular Applications
java --module-path mods -m com.example.app/com.example.app.Main
- Temporary Fix for Missing Dependencies
java --add-reads com.example.service=ALL-UNNAMED
Allows a named module to read all classpath modules.
- 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
overridesrequires
, 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.