1. Building modular projects: basic principles
Compiler and the module path (module-path)
Before modules existed, an application was compiled and run using the so‑called classpath — just a list of directories and JAR files where Java looked for classes. With the module system a new player appeared — module-path. Now the compiler and the JVM must know not only where classes are located, but also which modules they belong to, what dependencies they have, and which packages are exported.
Key point:
- For modular projects, use --module-path instead of -classpath.
- The compiler and the JVM must see all the modules your application needs (and their dependencies).
Example: manual compilation of modules
Suppose you have two modules: core and app, where app depends on core.
Directory layout:
project-root/
core/
src/
main/
java/
module-info.java
com/example/core/...
app/
src/
main/
java/
module-info.java
com/example/app/...
Manual compilation (demonstration):
# First compile core
javac -d out/core core/src/main/java/module-info.java core/src/main/java/com/example/core/*.java
# Then app, specifying module-path to core
javac --module-path out/core -d out/app app/src/main/java/module-info.java app/src/main/java/com/example/app/*.java
Run:
java --module-path out/core:out/app -m app/com.example.app.Main
In real projects you do not need to do this by hand — use Maven/Gradle or the IDE.
2. Maven: building multi-module projects
Multi-module project structure in Maven
Maven has long supported a structure with multiple modules (submodules). With the Java module system, these Maven “modules” often coincide with Java modules, but not always: a Maven module is just a subproject, while a Java module is what is described in module-info.java. Typically, one Maven module = one Java module.
Typical structure:
myproject/
pom.xml # Parent POM
core/
pom.xml # Module core
src/main/java/
module-info.java
com/example/core/...
app/
pom.xml # Module app
src/main/java/
module-info.java
com/example/app/...
Example parent POM (myproject/pom.xml):
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>myproject</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>core</module>
<module>app</module>
</modules>
</project>
Example POM for module core (core/pom.xml):
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>myproject</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>core</artifactId>
<dependencies>
<!-- There can be dependencies here, for example on other modules or third-party libraries -->
</dependencies>
</project>
Example POM for module app (app/pom.xml):
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>myproject</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>app</artifactId>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
Important:
- If app requires core, the module-info.java of module app must contain requires core;
- Maven will assemble dependencies automatically if <dependency> and <module> are configured correctly.
How Maven handles module-info.java
- Maven automatically compiles module-info.java along with the other classes.
- If you have modular dependencies, Maven will add them to the module-path at compile and run time.
- If you use old plugins or Java below 9, the module system will not work.
Running a modular application via Maven
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>21</release> <!-- or your Java version -->
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.example.app.Main</mainClass>
<commandlineArgs></commandlineArgs>
</configuration>
</plugin>
</plugins>
</build>
mvn clean install
mvn -pl app exec:java
Common mistakes when working with Maven and modules
Error: “module not found”. You did not specify the correct module-path or the dependent artifact is missing a <dependency>.
Duplicate module names. The project must not contain two modules with the same name (for example, two module core).
Missing export/requires. If you forget to export a package (exports) or declare a dependency (requires), you will get a compilation error.
Mixing classpath and module-path. Do not use outdated plugins/settings that do not support modules — this leads to unpredictable errors.
3. Gradle: building multi-module projects
Project structure in Gradle
myproject/
settings.gradle
build.gradle
core/
build.gradle
src/main/java/module-info.java
src/main/java/com/example/core/...
app/
build.gradle
src/main/java/module-info.java
src/main/java/com/example/app/...
settings.gradle
rootProject.name = 'myproject'
include 'core', 'app'
build.gradle (root)
subprojects {
apply plugin: 'java'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21) // or your Java version
}
}
repositories {
mavenCentral()
}
}
build.gradle for core
plugins {
id 'java'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
build.gradle for app
plugins {
id 'java'
id 'application'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
dependencies {
implementation project(':core')
}
application {
mainModule = 'app' // Your Java module name
mainClass = 'com.example.app.Main'
}
Build and run
./gradlew build
./gradlew :app:run
Gradle determines the module-path automatically if it finds module-info.java. If something does not work, check that you are using a recent version of Gradle and its plugins.
4. IntelliJ IDEA: creating and configuring a modular project
Creating a modular project
- File → New → Project → Java → enable “Create module-info.java”.
- For a multi-module project: File → New → Module — add new modules to the project.
- If you use Maven/Gradle, just import the project by pom.xml or build.gradle.
Structure in IDEA
- In Project View, each module is shown as a separate branch.
- Each module has its own module-info.java and its own sources.
- IDEA suggests which modules export/require which packages (exports/requires).
- If you forget to export a package or declare a dependency, IDEA will highlight an error.
Checking module-path, running the main class
- In Run/Debug Configurations specify:
- Main class (for example, com.example.app.Main)
- Module (for example, app)
- IDEA will take care of the module-path.
- If you run via Maven/Gradle — use the corresponding configurations (for example, app [run]).
Importing third-party modules and libraries
- To work with external libraries (for example, requires java.sql; or third-party JARs) — add a dependency via Maven/Gradle.
- If a library is not modular (no module-info.java), it will be automatically added to the “unnamed module” (this is fine for most libraries).
5. Practice: creating a simple multi-module project
Step 1. Create the project structure
myproject/
core/
src/main/java/module-info.java
src/main/java/com/example/core/HelloService.java
app/
src/main/java/module-info.java
src/main/java/com/example/app/Main.java
HelloService.java (core):
package com.example.core;
public class HelloService {
public String getHello() {
return "Hello from the core module!";
}
}
module-info.java (core):
module core {
exports com.example.core;
}
Main.java (app):
package com.example.app;
import com.example.core.HelloService;
public class Main {
public static void main(String[] args) {
HelloService service = new HelloService();
System.out.println(service.getHello());
}
}
module-info.java (app):
module app {
requires core;
}
Step 2. Build and run with Maven
In the project root, create the parent pom.xml and two subprojects (core/pom.xml, app/pom.xml) as above. In app/pom.xml add a dependency on core.
mvn clean install
mvn -pl app exec:java
Step 3. Build and run with Gradle
Create settings.gradle, the root build.gradle, and one per module. In app/build.gradle declare the dependency on core and set mainClass.
./gradlew build
./gradlew :app:run
Step 4. Import and run in IntelliJ IDEA
- Open the project root via File → Open.
- IDEA recognizes the Maven/Gradle structure and creates modules.
- In each module you can open module-info.java; IDEA will show relationships between modules.
- Run Main.java via the context menu (“Run Main.main()”) — IDEA will set the module-path automatically.
6. Common errors when building modular projects
Error #1: Incorrect module-path. If at run time or compile time you see a message like “module not found” or “package is not visible”, you most likely did not specify the correct module-path or forgot to add a dependency in pom.xml/build.gradle.
Error #2: Duplicate module names. The project must not contain two modules with the same name (for example, two module core). This leads to a compilation error.
Error #3: Non-exported package. If a class is in a package that is not exported via exports in module-info.java, it will be inaccessible to other modules even if it is public. A compilation error will occur when attempting to use such a class.
Error #4: Missing requires. If a module uses classes from another module but does not specify requires in module-info.java, the compiler will report an error.
Error #5: Incompatibility with external libraries. Mismatching modules and libraries without module-info.java (they end up in the “unnamed module”) usually does not get in the way, but in some configurations additional setup may be required.
Error #6: Launching from the wrong module. In the run configuration of IDEA or Maven/Gradle, an incorrect main class or module is specified — the application does not start. Check Main, mainModule/module, and the profile used.
GO TO FULL VERSION