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.javaFile - 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.examplepackage is accessible to other modules. - requires java.logging: Declares a dependency on the
java.loggingmodule.
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.sqlmodule. - requires java.xml: Indicates that this module depends on the
java.xmlmodule.
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.apipackage 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.basemodule, 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.MyServiceImplis an implementation of thecom.example.api.MyServiceservice. - uses com.example.api.MyService: Indicates that this module uses the
com.example.api.MyServiceservice.
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.utilpackage containing theStringUtilsclass. - com.example.mymodule: This module requires the
com.example.utilmodule and uses theStringUtilsclass 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.