Java Modules with JPMS
A module is a group of packages with a written list of its dependencies and its exported packages. JPMS, the Java Platform Module System, enforces that list at compile time and at run time.
greeter.lib declares its door
This file is src/greeter.lib/module-info.java. It names the library module and exports one package.
module greeter.lib {
exports com.greeter;
}The library class
This file is src/greeter.lib/com/greeter/Greeter.java. It holds the one class that the exported package offers.
package com.greeter;
public class Greeter {
public static String greet(String name) {
return "Hello, " + name;
}
}greeter.app declares its dependency
This file is src/greeter.app/module-info.java. It names the application module and requires the library module.
module greeter.app {
requires greeter.lib;
}The application class
This file is src/greeter.app/com/app/Main.java. Its main method prints the greeting from the library.
package com.app;
import com.greeter.Greeter;
class Main {
public static void main(String[] args) {
System.out.println(Greeter.greet("modules"));
}
}Example
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) throws Exception {
String javaHome = System.getProperty("java.home");
String javac = javaHome + "/bin/javac";
String java = javaHome + "/bin/java";
Path dir = Files.createTempDirectory("jpms-demo");
Path libDir = Files.createDirectories(dir.resolve("src/greeter.lib/com/greeter"));
Path appDir = Files.createDirectories(dir.resolve("src/greeter.app/com/app"));
Files.writeString(dir.resolve("src/greeter.lib/module-info.java"),
"module greeter.lib {\n exports com.greeter;\n}");
Files.writeString(libDir.resolve("Greeter.java"),
"package com.greeter;\npublic class Greeter {\n public static String greet(String name) {\n return \"Hello, \" + name;\n }\n}");
Files.writeString(dir.resolve("src/greeter.app/module-info.java"),
"module greeter.app {\n requires greeter.lib;\n}");
Files.writeString(appDir.resolve("Main.java"),
"package com.app;\nimport com.greeter.Greeter;\nclass Main {\n public static void main(String[] args) {\n System.out.println(Greeter.greet(\"modules\"));\n }\n}");
run(javac, "-d", dir.resolve("out").toString(), "--module-source-path", dir.resolve("src").toString(), "--module", "greeter.app");
String output = run(java, "--module-path", dir.resolve("out").toString(), "-m", "greeter.app/com.app.Main");
System.out.print(output);
}
static String run(String... command) throws Exception {
Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
String text;
try (InputStream in = process.getInputStream()) {
text = new String(in.readAllBytes());
}
if (process.waitFor() != 0) {
throw new IllegalStateException("Command failed: " + text);
}
return text;
}
}Key points
- A module-info.java file declares one module by name.
- requires names a module that this module depends on.
- exports opens one package to other modules.
- A package that is not exported stays hidden from other modules.
- Code on the classpath lives in the unnamed module and ignores module-info files.
- A module is a package of packages with a door list, and the compiler checks every door.
In this example, one program writes the four listed sources into a temporary folder, compiles both modules from a module source path, runs the application module, and prints the line it produces.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does a requires declaration do?