Introduction
Java modules were introduced in Java 9 as part of the Project Jigsaw. The module system, also known as the Java Platform Module System (JPMS), aims to provide a more scalable and flexible way to handle large applications and libraries. Modules help improve the structure, security, and maintainability of Java applications by enabling strong encapsulation and clear dependencies between different parts of the application.
Key Points:
- Encapsulation: Modules allow you to explicitly specify which parts of your code are accessible to other modules.
- Dependency Management: Modules provide a way to declare dependencies between different parts of your application, making it easier to manage and understand.
- Improved Security: By restricting access to internal APIs and only exposing necessary parts of your code, modules enhance the security of your application.
Table of Contents
- Creating a Module
- The
module-info.java
File - Defining Module Dependencies
- Exporting Packages
- Requiring Modules
- Using Services
- Real-World Example
- Conclusion
1. Creating a Module
To create a module, you need to structure your project with a module-info.java
file at the root of your module’s directory. This file contains the module declaration and specifies the module’s dependencies and exported packages.
Example:
my-module/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── com/
│ │ │ │ ├── example/
│ │ │ │ │ ├── MyModule.java
│ │ │ ├── module-info.java
├── build/
2. The module-info.java File
The module-info.java
file is where you define your module’s name, its dependencies, and the packages it exports.
Example:
module com.example.mymodule {
// Exporting a package
exports com.example;
// Requiring another module
requires java.logging;
}
Explanation:
- module com.example.mymodule: Declares a module named
com.example.mymodule
. - exports com.example: Specifies that the
com.example
package is accessible to other modules. - requires java.logging: Declares a dependency on the
java.logging
module.
3. Defining Module Dependencies
Modules can depend on other modules. You define these dependencies using the requires
keyword in the module-info.java
file.
Example:
module com.example.mymodule {
requires java.sql;
requires java.xml;
}
Explanation:
- requires java.sql: Indicates that this module depends on the
java.sql
module. - requires java.xml: Indicates that this module depends on the
java.xml
module.
4. Exporting Packages
To make a package accessible to other modules, you use the exports
keyword in the module-info.java
file.
Example:
module com.example.mymodule {
exports com.example.api;
}
Explanation:
- exports com.example.api: Makes the
com.example.api
package accessible to other modules.
5. Requiring Modules
Modules can specify dependencies on other modules using the requires
keyword. This helps to manage dependencies and ensures that all required modules are available at compile time and runtime.
Example:
module com.example.mymodule {
requires java.base;
requires com.external.library;
}
Explanation:
- requires java.base: All modules implicitly require the
java.base
module, but you can explicitly declare it if needed. - requires com.external.library: Declares a dependency on an external module named
com.external.library
.
6. Using Services
Modules can provide and consume services. This is useful for implementing the Service Provider Interface (SPI) pattern. You use the provides
and uses
keywords in the module-info.java
file.
Example:
module com.example.mymodule {
requires java.logging;
exports com.example.api;
// Providing a service
provides com.example.api.MyService with com.example.impl.MyServiceImpl;
// Consuming a service
uses com.example.api.MyService;
}
Explanation:
- provides com.example.api.MyService with com.example.impl.MyServiceImpl: Specifies that
com.example.impl.MyServiceImpl
is an implementation of thecom.example.api.MyService
service. - uses com.example.api.MyService: Indicates that this module uses the
com.example.api.MyService
service.
7. Real-World Example
Let’s create a simple example with two modules: com.example.mymodule
and com.example.util
.
com.example.util
Module
Directory Structure:
com.example.util/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── com/
│ │ │ │ ├── example/
│ │ │ │ │ ├── util/
│ │ │ │ │ │ ├── StringUtils.java
│ │ │ ├── module-info.java
├── build/
module-info.java
:
module com.example.util {
exports com.example.util;
}
StringUtils.java
:
package com.example.util;
public class StringUtils {
public static String reverse(String input) {
return new StringBuilder(input).reverse().toString();
}
}
com.example.mymodule
Module
Directory Structure:
com.example.mymodule/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── com/
│ │ │ │ ├── example/
│ │ │ │ │ ├── MyModule.java
│ │ │ ├── module-info.java
├── build/
module-info.java
:
module com.example.mymodule {
requires com.example.util;
}
MyModule.java
:
package com.example;
import com.example.util.StringUtils;
public class MyModule {
public static void main(String[] args) {
String original = "Hello, World!";
String reversed = StringUtils.reverse(original);
System.out.println("Original: " + original);
System.out.println("Reversed: " + reversed);
}
}
Explanation:
- com.example.util: This module exports the
com.example.util
package containing theStringUtils
class. - com.example.mymodule: This module requires the
com.example.util
module and uses theStringUtils
class to reverse a string.
8. Conclusion
Java modules provide a powerful way to encapsulate and manage dependencies in your Java applications. By defining module dependencies and exporting only the necessary packages, you can create more maintainable and secure applications. Understanding and utilizing the module system can significantly improve the organization and scalability of your projects.