A frequent headache developers face when working with Java Modules is the “package not visible” error, even though the dependency exists somewhere in the project. The culprit? Forgetting to propagate dependencies correctly. On the classpath, everything was accessible globally, but with JPMS, you must explicitly declare dependencies.
This is where requires transitive comes in. It allows dependency propagation so that downstream modules automatically gain access to the dependencies of the modules they depend on. This is particularly important in large enterprise systems, microservices, and layered architectures, where exposing stable APIs to multiple modules is critical.
What is requires transitive?
The requires transitive directive in module-info.java re-exports a dependency. Any module that depends on your module will also have access to the re-exported dependency without declaring it explicitly.
Example
// order module descriptor
module com.example.order {
requires transitive com.example.user;
exports com.example.order.api;
}
Here:
orderdepends onuser.- By marking it as
requires transitive, any module that depends onorderwill also automatically get access touser.
Real-World Example
Imagine a shopping application with three modules:
- User Module (
com.example.user)
module com.example.user {
exports com.example.user.api;
}
- Order Module (
com.example.order)
module com.example.order {
requires transitive com.example.user;
exports com.example.order.api;
}
- Payment Module (
com.example.payment)
module com.example.payment {
requires com.example.order;
exports com.example.payment.api;
}
What happens?
paymentdepends onorder.- Since
orderusesrequires transitiveforuser, thepaymentmodule automatically gets access touserAPIs. - Without
transitive,paymentwould have to declarerequires com.example.user;separately.
This reduces boilerplate and ensures dependency propagation across layers.
Pitfalls and Misuse Cases
- Overusing
requires transitive→ Leads to unnecessary exposure of internals and hidden coupling. - Leaky Abstractions → If a module re-exports too much, downstream modules may accidentally rely on unintended APIs.
- Version Conflicts → Complex graphs with transitive dependencies can cause ambiguity when versions differ.
- Wrong Placement → Declaring
requires transitivein every module defeats its purpose. Reserve it for stable API modules.
Best Practices
- Use
requires transitivefor API modules that are widely reused. - Avoid applying it to implementation details or unstable modules.
- Keep module boundaries clear to prevent dependency sprawl.
- Document why a dependency is made transitive in project guidelines.
- Regularly review
module-info.javato ensure only intentional propagation.
📌 What's New in Java Versions?
- Java 5 → N/A (modules introduced in Java 9)
- Java 9 → JPMS introduced with
requires transitivesupport - Java 11 → Better tooling to detect dependency issues in modular apps
- Java 17 → Minor refinements for JPMS performance/security checks
- Java 21 → No significant updates across Java versions for this feature
Analogy
Think of modules as office departments:
- The User Department provides employee data.
- The Order Department depends on User and shares its reports with Finance.
- The Payment Department depends on Order, and thanks to requires transitive, it automatically gets access to User data without needing a direct subscription.
This ensures smooth workflows without redundant requests.
Summary + Key Takeaways
requires transitivepropagates dependencies to downstream modules.- It’s ideal for shared API modules in layered systems.
- Avoid overuse, as it can create tight coupling and leaky abstractions.
- JPMS enforces explicit boundaries, making codebases cleaner, safer, and maintainable.
FAQs
Q1. What is the difference between the classpath and module path?
Classpath exposes everything globally; module path enforces explicit requires/exports.
Q2. Why do I get “package is not visible” errors when using modules?
Because the dependency wasn’t explicitly required or re-exported via requires transitive.
Q3. What is the purpose of requires transitive?
It propagates dependencies so downstream modules don’t need to declare them separately.
Q4. How do open and opens differ in reflection?
open moduleopens everything.opensopens specific packages, often restricted to frameworks.
Q5. What are automatic modules, and should I use them?
JARs without descriptors treated as modules. Use temporarily during migration, not in production.
Q6. How does JPMS improve security compared to classpath?
By hiding internals, controlling what’s exported, and limiting reflection.
Q7. When should I use jlink vs jmod?
jlinkbuilds slim runtime images.jmodpackages modules for distribution.
Q8. Can I migrate a legacy project to modules incrementally?
Yes, modularize one JAR at a time and use automatic modules as a bridge.
Q9. How do I handle third-party libraries that aren’t modularized?
Use them as automatic modules or place them on the classpath.
Q10. Do frameworks like Spring/Hibernate fully support modules?
Not fully. Many require opens for reflection to function correctly.