provides/uses (ServiceLoader) and opens for reflection
Decoupling implementations with services, and granting reflective access with opens.
The ServiceLoader pattern
JPMS has built-in support for service-provider decoupling. A *consumer* module declares the interface it needs:
module com.acme.app {
uses com.acme.spi.PaymentProcessor;
}
A *provider* module declares the implementation:
module com.acme.stripe {
requires com.acme.app;
provides com.acme.spi.PaymentProcessor
with com.acme.stripe.StripeProcessor;
}
At runtime, ServiceLoader.load(PaymentProcessor.class) discovers all provider modules on the module path without the consumer knowing the implementation class:
ServiceLoader<PaymentProcessor> loader =
ServiceLoader.load(PaymentProcessor.class);
for (PaymentProcessor p : loader) {
p.charge(amount);
}
The consumer and provider are loosely coupled — swap providers by changing which jar is on the module path, with no code changes.
opens — granting reflective access
Reflection can read private members, but JPMS blocks it by default even for exported packages. The opens directive grants reflective access:
module com.acme.app {
opens com.acme.app.model; // reflection allowed by all
opens com.acme.app.model to java.base; // qualified — only java.base
}
An open module (open module com.acme.app { ... }) opens every package for reflection — useful during migration. Frameworks like Hibernate and Spring use reflection heavily and often require opens on your entity/component packages.