Java 9 and the Module System: What Developers Need to Know
Java 9 and the Module System: What Developers Need to Know
Since September 21st, it's official: Java 9 is here. After multiple delays -- it was originally planned for 2016 -- arguably the biggest update to the Java platform since the introduction of Generics is finally available. At its core: the Java Platform Module System (JPMS), better known by its project name Jigsaw.
In this article, we examine what the module system concretely means, how the new module-info.java works, and what to watch out for when migrating existing projects.
What Is Project Jigsaw?
The module system solves a problem that has accompanied Java since its inception: the lack of encapsulation at the package level. Until now, any class declared as public could be used from anywhere on the classpath -- even if it was intended as an internal implementation detail. Who hasn't used classes from sun.misc.* or com.sun.* at some point, even though they were never intended as public API?
JPMS introduces a new layer above packages: Modules. A module explicitly defines:
- Which packages it makes visible to the outside (
exports) - Which other modules it depends on (
requires) - Which services it provides or consumes
With this, Java gains true encapsulation at the architectural level for the first time. The JDK library itself has been split into around 90 modules -- from java.base (which every module automatically imports) to java.sql to java.logging.
The module-info.java in Detail
Each module is described by a module-info.java file in the root directory of the source tree. The syntax is lean and readable:
module com.encircle360.backend {
// Declare dependencies
requires java.sql;
requires java.logging;
requires spring.core;
requires spring.context;
// Expose packages externally
exports com.encircle360.backend.api;
exports com.encircle360.backend.model;
// Internal packages remain encapsulated -- no exports needed
// com.encircle360.backend.internal is not visible from outside
// Selectively allow reflection (e.g., for frameworks)
opens com.encircle360.backend.model to spring.core;
}
An overview of the most important keywords:
requires-- Declares a dependency on another module. Withrequires transitive, the dependency is passed through to consuming modules.exports-- Makes a package visible to other modules. Only exported packages are accessible from outside.opens-- Allows reflection access to a package. This is particularly relevant for frameworks like Spring or Hibernate that rely heavily on reflection.provides ... with-- Registers a service implementation for the ServiceLoader API.uses-- Declares that the module consumes a particular service.
What Changes for Existing Projects?
The good news first: Java 9 is largely backward compatible. Existing code that does not use internal JDK APIs should run on Java 9 without changes in most cases. The so-called Unnamed Module ensures that code without a module-info.java continues to work -- everything on the classpath automatically ends up in this module and has access to all exported packages.
The less good news: anyone who has used internal JDK APIs will run into problems. sun.misc.Unsafe, sun.misc.BASE64Encoder, and similar classes are now encapsulated. The JVM still issues a warning rather than an error by default with --illegal-access=warn, but this is intended as a transitional measure. In future Java versions, access will be fully blocked.
Common Pitfalls During Migration
Split Packages: If two JARs contain classes in the same package, this no longer works in the module system. Each package may only be assigned to one module. In practice, this primarily affects projects that use older libraries with overlapping package names.
Reflection Access: Frameworks like Spring, Hibernate, or Jackson rely heavily on reflection. In the module system, reflection must be explicitly allowed via opens. If you want to open an entire module for reflection, you can use open module -- this is pragmatic but naturally a compromise on encapsulation.
Tooling and Build Systems: Maven and Gradle already offer basic support for Java 9 modules, but the integration is not yet mature everywhere. Especially with multi-module Maven projects, configuring the module path can be tricky.
Honest Assessment: Should You Migrate Now?
As of today -- just days after the release -- our recommendation is: Don't rush. Many popular libraries and frameworks do not yet offer full JPMS support. Spring Framework 5, which will officially support Java 9, has not yet been released as final. Hibernate, Jackson, Guava, and many other libraries are also still working on modularization.
For new greenfield projects, you can use Java 9 as a runtime and gradually introduce modules. For existing projects, we recommend:
- Update the runtime only first -- Use Java 9 as the runtime environment without defining your own modules
- Check your dependencies -- Use
jdepsto analyze which internal APIs are being used - Eliminate internal API usage -- Replace
sun.misc.BASE64Encoderwithjava.util.Base64and perform similar cleanup - Introduce modules gradually -- When all dependencies are ready
Other Highlights in Java 9
Besides the module system, Java 9 brings several other interesting features:
JShell (REPL): Java finally has an interactive shell. With jshell, you can try out Java code directly on the command line -- ideal for rapid prototyping and learning.
Private Methods in Interfaces: Since Java 8, interfaces can have default methods. Java 9 now also allows private methods in interfaces, improving code reuse within interface definitions.
Stream API Improvements: New methods like takeWhile(), dropWhile(), and ofNullable() make working with Streams more flexible.
HTTP/2 Client (Incubator): A new HTTP client with support for HTTP/2 and WebSockets is included as an incubator module. It is intended to eventually replace the aging HttpURLConnection.
Collection Factory Methods: Immutable collections can now be elegantly created: List.of("a", "b", "c"), Set.of(1, 2, 3), and Map.of("key", "value").
Conclusion
Java 9 is a significant release, and the module system will fundamentally change the way we structure Java applications in the long run. The benefits -- true encapsulation, clear dependencies, smaller runtime images through jlink -- are real and relevant.
At the same time, migration is not a walk in the park. The ecosystem needs time to catch up. Those who evaluate Java 9 now and prepare their codebase will find the transition much easier once the library landscape is ready.
At encircle360, we are actively supporting this transition and will report on our experiences migrating specific projects in future articles. If you have questions about Java 9 migration, feel free to reach out to us.
Written by
Patrick HütterFounder & Software Architect
Software architect, engineer and entrepreneur. Patrick has been building products and platforms for over a decade — from enterprise backends and cloud-native infrastructure to AI-powered applications. As founder of encircle360, he combines deep technical expertise with entrepreneurial vision, driving open source projects that create real impact.
You might also like
From Manifest to Production: How ADL, A2A and the Inference Gateway Are Revolutionizing Agent Infrastructure
Jul 5, 2026 · 12 min read
Agent Orchestration with Java: Bringing LLM Agents to Production on the JVM
Jul 4, 2026 · 6 min read
Spring AI: How Java Developers Can Finally Integrate AI Features the Right Way
Mar 25, 2026 · 5 min read