Java - Java Module System (JPMS) – Detailed Explanation

The Java Module System, officially introduced in Java 9, is a major enhancement to the Java platform that brings modularity to Java applications. It is also commonly referred to as JPMS (Java Platform Module System). Before this, Java applications were organized using packages and JAR files, but there was no strong mechanism to control dependencies or encapsulation at a higher level. JPMS addresses these limitations.


1. What is a Module?

A module is a self-contained unit of code that includes:

  • Packages (classes and interfaces)

  • A description of its dependencies

  • A definition of which parts are accessible to other modules

Each module is defined by a special file called:

module-info.java

This file acts as the module’s descriptor and is placed at the root of the module.


2. Structure of a Module

A typical module structure looks like this:

  • module-info.java

  • com/example/package1/

  • com/example/package2/

The module-info.java file contains directives that define:

  • Module name

  • Required dependencies

  • Exported packages

Example:

module com.example.myapp {
    requires java.sql;
    exports com.example.myapp.api;
}

3. Key Directives in JPMS

requires

This specifies dependencies on other modules.

Example:

requires java.base;

Every module implicitly depends on java.base, which contains fundamental classes like String and Object.


exports

This makes a package accessible to other modules.

Example:

exports com.example.service;

If a package is not exported, it is completely hidden from other modules, even if the classes are public.


requires transitive

This allows dependency propagation.

Example:

requires transitive java.logging;

If Module A requires Module B transitively, and Module C depends on Module A, then Module C automatically reads Module B.


opens

This is used for reflection.

Example:

opens com.example.model;

It allows runtime access (for frameworks like Hibernate or Spring) without making the package publicly accessible at compile time.


uses and provides

These support service-based architecture.

Example:

uses com.example.service.PaymentService;
provides com.example.service.PaymentService with com.example.impl.PaymentServiceImpl;

This enables loose coupling using service loaders.


4. Strong Encapsulation

One of the most important features of JPMS is strong encapsulation.

Before JPMS:

  • Public classes were accessible everywhere.

With JPMS:

  • Only exported packages are accessible.

  • Internal implementation details remain hidden.

This improves:

  • Security

  • Maintainability

  • Code clarity


5. Benefits of JPMS

Better Dependency Management

Modules explicitly declare dependencies, reducing hidden or accidental dependencies.

Improved Security

Internal packages are hidden unless explicitly exported.

Faster Startup and Smaller Applications

Unused modules can be excluded, reducing runtime footprint.

Reliable Configuration

Eliminates issues like classpath conflicts and “JAR hell.”


6. Module Path vs Classpath

JPMS introduces a new concept called module path.

  • Classpath: Traditional way of loading classes (no structure or dependency rules)

  • Module Path: Structured, enforces module boundaries and dependencies

When using JPMS, applications are typically run using the module path instead of the classpath.


7. Types of Modules

Named Modules

Modules with a module-info.java file.

Automatic Modules

Existing JARs placed on the module path without module-info.java. Their names are derived automatically.

Unnamed Module

Code on the classpath belongs here. It has no module name and can access everything but cannot be accessed by named modules in a controlled way.


8. Migration to JPMS

Migrating existing applications involves:

  • Identifying dependencies

  • Creating module-info.java

  • Resolving split packages and cyclic dependencies

Challenges may include:

  • Legacy libraries not supporting modules

  • Reflection-related issues


9. Real-World Usage

JPMS is widely used in:

  • Large enterprise systems

  • Microservices architectures

  • Framework development

It is especially useful when building scalable and maintainable applications with clear boundaries between components.


10. Limitations

  • Initial learning curve is high

  • Not all third-party libraries fully support modules

  • Can introduce complexity in small applications


Conclusion

The Java Module System is a powerful feature that transforms how Java applications are structured. By introducing modular architecture, it improves dependency management, encapsulation, and scalability. While it may not be necessary for small projects, it becomes highly valuable in large and complex systems where maintainability and reliability are critical.