11 Refining dependencies and APIs
This chapter covers
- Handling dependencies that are part of a module’s API
- Aggregating and refactoring modules without breaking clients
- Defining optional dependencies
- Writing code in the face of absent dependencies
- Exporting packages to selected modules only
Chapter 3 explains how requires and exports directives are the basis for readability and accessibility. But these mechanisms are strict: every module has to be explicitly required, all required modules have to be present for the application to compile and launch, and exported packages are accessible to all other modules. This suffices for the majority of use cases, but there’s still a significant portion in which these solutions are too broad.
The most obvious use case is optional dependencies, which a module wants to compile against but which aren’t necessarily present at run time. Spring, for example, does this with the Jackson databind library. If you run a Spring application and want to use JSON as a data-transfer format, you can get support for that by dropping in the Jackson artifact. If, on the other hand, that artifact is absent, Spring is still happy—it doesn’t support JSON then. Spring uses Jackson but doesn’t require it.