10.2 Module Directives: requires, exports, opens, provides, and uses
Key Takeaways
- The requires directive establishes direct module readability, while requires transitive grants implied readability, automatically propagating module access to downstream consumers.
- The requires static directive declares a mandatory compile-time dependency that becomes optional at runtime, enabling optional feature sets without runtime ClassNotFoundException failures.
- The exports directive makes public types in a package accessible at compile-time and runtime, and qualified exports (exports ... to) restrict this accessibility exclusively to named target modules.
- Exporting a package is non-recursive: exporting com.app does not export subpackages like com.app.model, and wildcard export syntax is strictly illegal.
- The opens directive enables deep reflective access to private members of a package at runtime without exposing public types at compile-time, while uses and provides with configure decoupled ServiceLoader service provider bindings.
Module Directives: requires, exports, opens, provides, and uses
A module descriptor (module-info.java) controls access boundaries, dependencies, and service bindings through explicit module directives. On the 1Z0-830 exam, questions test the precise differences between module targets versus package targets, implied readability mechanics (requires transitive), optional compile-time dependencies (requires static), qualified visibility, non-recursive export rules, and reflective permissions.
1. Dependency Directives: requires, requires transitive, and requires static
To consume types declared in another module, a module must declare readability using the requires family of directives.
1. Basic requires (Direct Readability)
module com.app.billing {
requires java.sql; // Target is a MODULE name
requires com.app.inventory; // Target is a MODULE name
}
- Syntax:
requires <module-name>; - Rule: The target must be a module name, never a package name or class name. Writing
requires java.sql.DriverManager;orrequires java.util;results in a compile-time error. - Readability: Grants the declaring module the ability to read and access exported public types from
<module-name>. - Acyclicity: JPMS strictly prohibits cyclic dependencies. If
module A requires B;andmodule B requires A;, both compilation (javac) and runtime startup (java) fail with a cyclic dependency error.
2. requires transitive (Implied Readability)
Implied readability allows a module to pass along its dependency to any downstream module that reads it.
// Module: com.app.repository
module com.app.repository {
requires transitive java.sql; // Downstream consumers gain readability to java.sql
exports com.app.repository.api;
}
// Module: com.app.web
module com.app.web {
requires com.app.repository; // Implicitly gains readability to java.sql!
}
- Semantics: If Module X declares
requires transitive Y;, and Module Z declaresrequires X;, then Module Z automatically gains readability to Module Y without needing to writerequires Y;in its own descriptor. - Mandatory Use Case (API Leakage): When an exported class in Module X exposes a type from Module Y in its
publicorprotectedmethod signatures (return types, parameter types, field types, or exceptions), Module X must declarerequires transitive Y;. Withouttransitive, downstream consumers calling that method cannot compile because they cannot resolve the returned type.
package com.app.repository.api;
import java.sql.Connection; // From java.sql module
public class DatabaseHelper {
// Public method returns java.sql.Connection!
// com.app.repository MUST declare 'requires transitive java.sql;'
public Connection getActiveConnection() {
return null;
}
}
3. requires static (Compile-Time Optional Dependency)
module com.app.analytics {
requires static lombok; // Needed during compilation only
requires static com.optional.profiler; // Optional at runtime
}
- Semantics: The dependency is mandatory during compilation (
javac), but optional at runtime (java). - If the required module is absent on the module path when launching the JVM, the runtime resolves the module graph cleanly without errors.
- If the application executes code at runtime that attempts to load classes from the missing module, a
NoClassDefFoundErrororClassNotFoundExceptionis thrown. Code should defensively check for class availability before invocation (e.g., viaClass.forName()ortry-catch). - Modifiers Combination: You can combine modifiers:
requires static transitive <module-name>;orrequires transitive static <module-name>;.
2. Encapsulation Directives: exports and Qualified exports ... to
Standard exports
module com.app.finance {
exports com.app.finance.services; // Target is a PACKAGE name
exports com.app.finance.dto;
}
- Syntax:
exports <package-name>; - Rule: The target must be a package name, never a module or class name.
- Accessibility: Exposes all
publicandprotectedclasses, interfaces, records, and enums in<package-name>to any module that readscom.app.finance. - No Subpackage Inheritance: Exporting a package does not export its subpackages. For example,
exports com.app.finance;does not exportcom.app.finance.servicesorcom.app.finance.internal. Every subpackage must have its ownexportsdirective. - No Wildcards: Wildcard syntax like
exports com.app.finance.*;is strictly forbidden by the Java compiler. - Internal Encapsulation: Non-public members, package-private classes, and all types inside non-exported packages remain strictly inaccessible outside the module.
Qualified Exports (exports ... to)
module com.app.core {
// Only com.app.admin and com.app.billing can access com.app.core.internal
exports com.app.core.internal to com.app.admin, com.app.billing;
}
- Restricts package accessibility strictly to the specified comma-separated list of target module names.
- Any module not in the
tolist cannot access the package at compile time or runtime.
3. Reflection Directives: opens and Qualified opens ... to
Frameworks relying on reflection (e.g., ORMs, JSON serializers, dependency injectors) often need access to private fields and constructors without exposing those internal implementation details as a public compile-time API.
module com.app.model {
exports com.app.model.api; // Accessible at compile time and runtime
opens com.app.model.entities; // Runtime reflection only!
opens com.app.model.dto to com.fasterxml.jackson.databind; // Qualified opens
}
- Syntax:
opens <package-name>;oropens <package-name> to <module-name-1>, <module-name-2>; - Target: Must be a package name (with target module names in the
toclause for qualified opens). - Compile-Time Isolation: Code in other modules cannot compile against classes in opened-only packages.
javactreats unopened and opened-only packages as hidden. - Runtime Reflection: Allows other modules (or specified modules in qualified opens) to perform deep reflection (
AccessibleObject.setAccessible(true)) onprivate,protected, and package-private elements within<package-name>. - Open Module Constraint: You cannot use
opensinside anopen module(causes a compile error).
4. Service Directives: uses and provides ... with
JPMS integrates directly with java.util.ServiceLoader to support decoupled Service Provider Interfaces (SPI).
// Service Consumer Module Descriptor
module com.app.client {
requires com.app.spi; // Module containing the interface
uses com.app.spi.PaymentGateway; // Target is fully qualified INTERFACE/CLASS
}
// Service Provider Module Descriptor
module com.app.stripe.provider {
requires com.app.spi;
provides com.app.spi.PaymentGateway
with com.app.stripe.StripePaymentGateway; // Target is SPI and Implementation
}
uses <interface-or-class-name>;:- Informs the runtime that the declaring module consumes implementations of the specified interface or abstract class via
ServiceLoader.load(). - The target must be a fully qualified type name, not a module or package.
- Informs the runtime that the declaring module consumes implementations of the specified interface or abstract class via
provides <interface-name> with <implementation-class-1>, <implementation-class-2>;:- Informs the runtime that this module supplies one or more concrete implementations for the service interface.
- Multiple implementations are separated by commas.
- The implementation class does not need to be exported in an
exportsdirective.
5. Directives Master Reference & Exam Trap Matrix
| Directive | Target Entity | Valid Target Type | Compile-Time Visibility | Runtime Deep Reflection |
|---|---|---|---|---|
requires M; | Module | Module name | Exported types of M | Only if opened by M |
requires transitive M; | Module | Module name | Exported types of M + passes to consumers | Only if opened by M |
requires static M; | Module | Module name | Exported types of M (Mandatory) | Optional at runtime |
exports P; | Package | Package name | public/protected types to all | Public types only |
exports P to M1, M2; | Package | Package name (to Modules) | public/protected types to M1, M2 | Public types to M1, M2 |
opens P; | Package | Package name | None (Hidden from javac) | All members (including private) |
opens P to M1; | Package | Package name (to Module) | None (Hidden from javac) | All members to M1 only |
uses S; | Type | Interface or Class name | N/A (Service Consumer) | Via ServiceLoader |
provides S with C; | Types | Interface with Concrete Class | N/A (Service Provider) | Instantiated by ServiceLoader |
Module com.data.service contains an exported public class with the method public java.sql.Connection createConnection(). Module com.data.app declares requires com.data.service; and attempts to compile a class calling createConnection(). How must com.data.service declare its dependency on java.sql so that com.data.app compiles without an explicit requires java.sql; directive?
Which of the following correctly distinguishes the exports directive from the opens directive in a standard named module descriptor?
A developer writes exports com.myapp.orders; in a module-info.java file and expects classes in com.myapp.orders.model and com.myapp.orders.util to be accessible to external modules. What is the behavior in JPMS?
An application relies on a bytecode instrumentation framework for code analysis during compilation, but this framework is not required when running in production. Which directive should be used in module-info.java?