The Java Module System
Nicolai Parlog
Foreword by Kevlin Henney
  • June 2019
  • ISBN 9781617294280
  • 440 pages
  • printed in black & white

Nicolai’s care and attention to detail allow you to take advantage of the distillation of his knowledge—from theory to practice, from entry level to advanced.

From the Foreword by Kevlin Henney

Java's much-awaited "Project Jigsaw" is finally here! Java 11 includes a built-in modularity framework, and The Java Module System is your guide to discovering it. In this new book, you'll learn how the module system improves reliability and maintainability, and how it can be used to reduce tight coupling of system components.

About the Technology

Packaging code into neat, well-defined units makes it easier to deliver safe and reliable applications. The Java Platform Module System is a language standard for creating these units. With modules, you can closely control how JARs interact and easily identify any missing dependencies at startup. This shift in design is so fundamental that starting with Java 9, all core Java APIs are distributed as modules, and libraries, frameworks, and applications will benefit from doing the same.

About the book

The Java Module System is your in-depth guide to creating and using Java modules. With detailed examples and easy-to-understand diagrams, you’ll learn the anatomy of a modular Java application. Along the way, you’ll master best practices for designing with modules, debugging your modular app, and deploying to production.

Table of Contents detailed table of contents

Part 1: Hello, modules

1. First piece of the puzzle

1.1. What is modularity all about?

1.1.1. Visualizing software as graphs

1.1.2. The impact of design principles

1.1.3. What modularity is all about

1.2. Module erasure before Java 9

1.3. Complications before Java 9

1.3.1. Unexpressed dependencies between JARs

1.3.2. Shadowing of classes with the same name

1.3.3. Conflicts between different versions of the same project

1.3.4. Complex class loading

1.3.5. Weak encapsulation across JARs

1.3.6. Security checks have to be handcrafted

1.3.7. Poor startup performance

1.3.8. Rigid Java runtime

1.4. Bird’s-eye view on the module system

1.4.1. Everything is a module

1.4.2. Your first module

1.4.3. The module system in action

1.4.4. Your non-modular project will be fine—​mostly

1.5. Goals of the module system

1.5.1. Reliable configuration—​leaving no JAR behind

1.5.2. Strong encapsulation—​making module-internal code inaccessible

1.5.3. Automated security and improved maintainability

1.5.4. Improved startup performance

1.5.5. Scalable Java platform

1.5.6. Non-Goals

1.6. Skills, old and new

1.6.1. What you will learn

1.6.2. What you should know

1.7. Summary

2. Anatomy of a modular application

2.1. Introducing the ServiceMonitor

2.2. Modularizing the ServiceMonitor

2.2.1. Cutting the ServiceMonitor into modules

2.2.2. Laying files out in a directory structure

2.2.3. Declaring and describing modules

2.2.4. Writing code is much like before

2.2.5. Compiling and packaging modules

2.2.6. Running the ServiceMonitor

2.2.7. Extending a modular code base

2.3. Post mortem: effects of the module system

2.3.1. What the module system does for us

2.3.2. What else the module system can do for us

2.4. Summary

3. Defining modules and their properties

3.1. Modules—​the building blocks of modular applications

3.1.1. Java modules (JMODs)--shipped with the JDK

3.1.2. Modular JARs—​home-grown modules

3.1.3. Module declarations—​defining a module’s properties

3.1.4. The many types of modules

3.2. Readability: Connecting the pieces

3.2.1. Achieving reliable configuration

3.2.2. Experimenting with unreliable configurations

3.3. Accessibility: Defining public APIs

3.3.1. Achieving strong encapsulation

3.3.2. Encapsulating transitive dependencies

3.3.3. Encapsulation skirmishes

3.4. The module path: Letting Java know about modules

3.4.1. Module resolution: Analysis and verification of an application’s structure

3.4.2. Module graph: Representation of an application’s structure

3.4.3. Adding modules to the graph

3.4.4. Adding edges to the graph

3.4.5. Accessibility is an ongoing effort

3.5. Summary

4. Building modules from source to JAR

4.1. Organizing your project in a directory structure

4.1.1. New proposal—​new convention?

4.1.2. Established directory structure

4.1.3. The place for module declarations

4.2. Compiling a single module

4.2.1. Compiling modular code

4.2.2. Modular or non-modular?

4.3. Compiling multiple modules

4.3.1. The naive approach

4.3.2. The module source path: Informing the compiler of the project structure

4.3.3. The asterisk as a token for the module name

4.3.4. Multiple module source path entries

4.3.5. Setting the initial module

4.3.6. Is it worth it?

4.4. Compiler options

4.5. Packaging a modular JAR

4.5.1. Quick recap of jar

4.5.2. Analyzing a JAR

4.5.3. Defining an entry point

4.5.4. Archiver options

4.6. Summary

5. Running and debugging modular applications

5.1. Launching the JVM with modules

5.1.1. Specifying the main class

5.1.2. Passing parameters to the application

5.2. Loading resources from modules

5.2.1. Resource loading before Java 9

5.2.2. Resource loading on Java 9 and later

5.2.3. Loading package resources across module boundaries

5.3. Debugging modules and modular applications

5.3.1. Analyzing individual modules

5.3.2. Validating sets of modules

5.3.3. Validating a module graph

5.3.4. Listing observable modules and dependencies

5.3.5. Excluding modules during resolution

5.3.6. Observing the module system with log messages

5.4. Java Virtual Machine options

5.5. Summary

Part 2: Adapting real-world projects

6. Compatibility challenges when moving to Java 9

6.1. Working with JEE modules

6.1.1. Why are the JEE modules special?

6.1.2. Manually resolving JEE modules

6.1.3. Dropping in third-party implementations of JEE modules

6.2. Casting to URLClassLoader

6.2.1. Application class loaders, then and now

6.2.2. Getting by without URLClassLoader

6.2.3. Finding troublesome casts

6.3. Updated runtime image directory layout

6.4. Selecting, replacing, and extending the platform

6.4.1. No more compact profiles

6.4.2. Extension mechanism was removed

6.4.3. Endorsed standards override mechanism was removed

6.4.4. Some boot class path options were removed

6.4.5. No compilation for Java 5

6.4.6. JRE version selection was removed

6.5. The little things that make big things fail

6.5.1. New Version Strings

6.5.2. Tool exodus

6.5.3. The littlest things

6.5.4. New deprecations in Java 9, 10, and 11

6.6. Summary

7. Recurring challenges when running on Java 9 or later

7.1. Encapsulation of internal APIs

7.1.1. Internal APIs under the microscope

7.1.2. Analyzing dependencies with JDeps

7.1.3. Compiling against internal APIs

7.1.4. Executing against internal APIs

7.1.5. Compiler and JVM options for accessing internal APIs

7.2. Mending split packages

7.2.1. What is the problem with split packages?

7.2.2. The effects of split packages

7.2.3. Many ways to handle split packages

7.2.4. Patching modules: Last resort to handling split packages

7.2.5. Finding split packages with JDeps

7.2.6. A note on dependency version conflicts

7.3. Summary

8. Incremental modularization of existing projects

8.1. Why incremental modularization is even an option

8.1.1. If every JAR needed to be modular…​?

8.1.2. Mixing and matching plain JARs with modules

8.1.3. Technical underpinnings of incremental modularization

8.2. The unnamed module: a.k.a the class path

8.2.1. The chaos of the class path: Captured by the unnamed module

8.2.2. Module resolution for the unnamed module

8.2.3. Depending on the unnamed module

8.3. Automatic modules: Plain JARs on the module path

8.3.1. Automatic module names: Small detail, big impact

8.3.2. Module resolution for automatic modules

8.3.3. All-in on automatic modules?

8.3.4. Depending on automatic modules

8.4. Summary

9. Migration and modularization strategies

9.1. Migration strategies

9.1.1. Preparatory updates

9.1.2. Estimating the effort

9.1.3. Continuously build on Java 9

9.1.4. Thoughts on command line options

9.2. Modularization strategies

9.2.1. Bottom-up modularization—​if all project dependencies are modular

9.2.2. Top-down modularization—​if your application can’t wait for its dependencies

9.2.3. Inside-out modularization—​if your project is in the middle of the stack

9.2.4. Applying these strategies within your project

9.3. Making JARs modular

9.3.1. Open modules as an intermediate step

9.3.2. Generating module declarations with JDeps

9.3.3. Hacking third-party JARs

9.3.4. Publishing modular JARs for Java 8 and older

9.4. Summary

Part 3: Advanced module system features

10. Using services to decouple modules

10.1. Services in the Java Platform Module System

10.1.1. Using, providing, and consuming services

10.1.2. Module resolution for services

10.2. Designing services well

10.2.1. Types that can be services

10.2.2. Using factories as services

10.2.3. Isolating consumers from global state

10.2.4. Organizing services, consumers, and providers into modules

10.2.5. Using services to break cyclic dependencies

10.2.6. Declaring services across different Java versions

10.3. Accessing services with the ServiceLoader API

10.3.1. Loading and accessing services

10.3.2. Idiosyncrasies of loading services

10.4. Summary

11. Refining dependencies and APIs

11.1. Implied readability: Passing dependencies on

11.1.1. Exposing a module’s dependencies

11.1.2. The transitive modifier: Implying readability on a dependency

11.1.3. When to use implied readability

11.1.4. When to rely on implied readability

11.1.5. Refactoring modules with implied readability

11.2. Optional dependencies

11.2.1. The conundrum of reliable configuration

11.2.2. The static modifier: Marking dependencies as optional

11.2.3. Module resolution of optional dependencies

11.2.4. Coding against optional dependencies

11.3. Qualified exports: Limiting accessibility to specific modules

11.3.1. Exposing internal APIs

11.3.2. Exporting packages to modules

11.3.3. When to use qualified exports

11.3.4. Exporting packages on the command line

11.4. Summary

12. Reflection in a modular world

12.1. Why exports directives are no good fit for reflection

12.1.1. Breaking into non-modular code

12.1.2. Forcing the publication of internal types

12.1.3. Qualified exports create coupling to specific modules

12.1.4. No support for deep reflection

12.2. Open packages and modules: Designed for the reflection use case

12.2.1. Opening packages to run-time access

12.2.2. Opening packages for specific modules

12.2.3. Exporting versus opening packages

12.2.4. Opening modules: Reflection closeout

12.3. Reflecting over modules

12.3.1. Updating reflecting code for modules (or not)

12.3.2. Using variable handles instead of reflection

12.3.3. Analyzing module properties with reflection

12.3.4. Modifying module properties with reflection

12.3.5. Forwarding open packages

12.4. Dynamically creating module graphs with layers

12.4.1. What are layers?

12.4.2. Analyzing layers

12.4.3. Creating module layers

12.5. Summary

13. Module versions: What’s possible and what’s not

13.1. The lack of version support by the JPMS

13.1.1. No support for multiple versions

13.1.2. No support for version selection

13.1.3. What the future may bring

13.2. Recording version information

13.2.1. Recording versions while building modules

13.2.2. Accessing module versions

13.3. Running multiple versions of a module in separate layers

13.3.1. Why you need a starter to spin up additional layers

13.3.2. Spinning up layers for your application, Apache Twill, and Cassandra Java Driver

13.4. Summary

14. Customizing runtime images with jlink

14.1. Creating custom runtime images

14.1.2. Image content and structure

14.1.3. Including services in runtime images

14.2. Creating self-contained application images

14.2.1. Including application modules in images

14.2.2. Generating a native launcher for your application

14.2.3. Security, performance, and stability

14.3. Generating images across operating systems

14.4.2. Reducing image size

14.4.3. Improving runtime performance

14.6. Summary

15. Putting the pieces together

15.1. Adding bells and whistles to ServiceMonitor

15.1.1. Diversified dependencies

15.1.2. Reduced visibility

15.1.3. Decoupled with services

15.1.4. Loads code at run time with layers

15.1.5. Handles dependencies on plain JARs

15.2. Tips for a modular application

15.2.1. Modular or not?

15.2.2. The ideal module

15.2.3. Take care of your module declarations

15.2.4. Breaking code by editing module declarations

15.3. The technology landscape

15.3.1. Java 9 beyond the module system

15.3.2. Maven, Gradle, and other build tools

15.3.3. OSGi

15.3.4. Microservices

15.4. Thoughts on a modular ecosystem


Appendix A: Class-path recap

A.1. Using the class path to load application JARs

A.2. The class path since Java 9

A.3. Summary

Appendix B: High-level introduction to the reflection API

B.1. Fundamental types and methods

B.2. Breaking into APIs with setAccessible

B.3. Annotations mark code for reflection

B.4. Summary

Appendix C: Observing the JVM with unified logging

C.1. What is unified logging?

C.2. Defining which messages should be shown

C.3. Defining where messages should go

C.4. Defining what messages should say

C.5. Configuring the entire logging pipeline

C.6. Summary

Appendix D: Analyzing a project’s dependencies with JDeps

D.1. Getting to know JDeps

D.2. Including dependencies in the analysis

D.3. Configuring JDeps' output

D.4. Drilling deeper into your project’s dependencies

D.5. JDeps understands modules

D.6. Summary

Appendix E: Targeting multiple Java versions with multi-release JARs

E.1. Creating a multi-release JAR

E.2. Internal workings of multi-release JARs

E.3. Usage recommendations

E.3.1. Organizing the source code

E.3.2. Organizing the byte code

E.3.3. When to use multi-release JARs

E.4. Summary

What's inside

  • The anatomy of a modular Java app
  • Building modules from source to JAR
  • Migrating to modular Java
  • Decoupling dependencies and refining APIs
  • Handling reflection and versioning
  • Customizing runtime images
  • Updated for Java 11

About the reader

Perfect for developers with some Java experience.

About the author

Nicolai Parlog is a developer, author, speaker, and trainer. His home is

We interviewed Nicolai as a part of our Six Questions series. Check it out here.

placing your order...

Don't refresh or navigate away from the page.
print book $29.99 $49.99 pBook + eBook + liveBook
Additional shipping charges may apply
The Java Module System (print book) added to cart
continue shopping
go to cart

eBook $31.99 $39.99 3 formats + liveBook
The Java Module System (eBook) added to cart
continue shopping
go to cart

Prices displayed in rupees will be charged in USD when you check out.
customers also reading

This book 1-hop 2-hops 3-hops

FREE domestic shipping on three or more pBooks