The 30 second answer
Java 9 added modules. A module descriptor says what the module is called, which modules it needs, and which packages it exports. That gives two things: missing dependencies fail at startup instead of at runtime, and packages that are not exported are truly hidden, even from reflection by default. The JDK itself was split into modules, which also lets you build a small custom runtime with jlink.
A module is declared in module-info.java:
module com.shop.orders {
requires java.sql;
requires transitive com.shop.common;
exports com.shop.orders.api;
opens com.shop.orders.model to com.fasterxml.jackson.databind; // deep reflection
}Goals from JEP 261:
- Reliable configuration: the module graph is resolved at startup, so a missing module fails fast instead of producing
NoClassDefFoundErrorlater. - Strong encapsulation: only exported packages are accessible;
opensis needed for deep reflection by frameworks.
The JDK was modularized (JEP 200), so jlink (JEP 282) can build a runtime image containing only the modules an app needs, which helps container images.
Code that isn't modularized still works: it runs on the class path in the unnamed module. [Opinion] Most Spring Boot applications still run on the class path; for them the practical impact of JPMS is mainly the strong encapsulation of JDK internals.
Sources: JEP 261: Module System, JEP 200: The Modular JDK, JEP 282: jlink