1. Budowanie projektów modułowych: podstawowe zasady
Kompilator i ścieżka modułów (module-path)
Przed pojawieniem się modułów całą aplikację kompilowano i uruchamiano za pomocą tzw. classpath — po prostu listy katalogów i plików JAR, w których Java szukała klas. W systemie modułów pojawił się nowy gracz — module-path. Teraz kompilator i JVM muszą wiedzieć nie tylko, gdzie leżą klasy, ale też do jakich modułów należą, jakie mają zależności i które pakiety są eksportowane.
Kluczowa kwestia:
- Dla projektów modułowych używaj --module-path zamiast -classpath.
- Kompilator i JVM muszą widzieć wszystkie moduły potrzebne twojej aplikacji (oraz ich zależności).
Przykład: ręczna kompilacja modułów
Załóżmy, że masz dwa moduły: core i app, gdzie app zależy od core.
Struktura katalogów:
project-root/
core/
src/
main/
java/
module-info.java
com/example/core/...
app/
src/
main/
java/
module-info.java
com/example/app/...
Ręczna kompilacja (demonstracja):
# Najpierw kompilujemy core
javac -d out/core core/src/main/java/module-info.java core/src/main/java/com/example/core/*.java
# Potem app, wskazując module-path na core
javac --module-path out/core -d out/app app/src/main/java/module-info.java app/src/main/java/com/example/app/*.java
Uruchomienie:
java --module-path out/core:out/app -m app/com.example.app.Main
W prawdziwych projektach nie ma potrzeby robić tego ręcznie — używaj Maven/Gradle lub IDE.
2. Maven: budowanie projektów wielomodułowych
Struktura projektu wielomodułowego w Maven
Maven od dawna wspiera strukturę z kilkoma modułami (submodules). Teraz, wraz z pojawieniem się systemu modułów Javy, te „moduły” Mavena często pokrywają się z modułami Javy, ale nie zawsze: moduł Mavena to po prostu podprojekt, a moduł Javy to to, co jest opisane w module-info.java. Zwykle jeden moduł Mavena = jeden moduł Javy.
Typowa struktura:
myproject/
pom.xml # POM nadrzędny
core/
pom.xml # moduł core
src/main/java/
module-info.java
com/example/core/...
app/
pom.xml # moduł app
src/main/java/
module-info.java
com/example/app/...
Przykład 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>
Przykład POM dla modułu core (core/pom.xml):
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>myproject</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>core</artifactId>
<dependencies>
<!-- Tutaj mogą być zależności, np. do innych modułów lub zewnętrznych bibliotek -->
</dependencies>
</project>
Przykład POM dla modułu 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>
Ważne:
- Jeśli w app wymagany jest core, w module-info.java modułu app powinno być requires core;
- Maven sam zbuduje zależności, jeśli poprawnie wpisane są <dependency> i <module>.
Jak Maven uwzględnia module-info.java
- Maven automatycznie kompiluje module-info.java wraz z pozostałymi klasami.
- Jeśli masz zależności modułowe, Maven doda je do module-path podczas kompilacji i uruchamiania.
- Jeśli używasz starych wtyczek lub Javy poniżej 9 — system modułów nie będzie działał.
Uruchamianie aplikacji modułowej przez Maven
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>21</release> <!-- albo twoja wersja Javy -->
</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
Typowe błędy podczas pracy z Mavenem i modułami
Błąd: „module not found”. Nie podano poprawnego module-path lub nie wpisano <dependency> dla zależnego artefaktu.
Duplikowanie nazw modułów. W projekcie nie powinno być dwóch modułów o tych samych nazwach (np. dwa module core).
Brak export/requires. Jeśli zapomniano wyeksportować pakiet (exports) lub zadeklarować zależność (requires), pojawi się błąd kompilacji.
Mieszanie classpath i module-path. Nie używaj przestarzałych wtyczek/ustawień, które nie wspierają modułów: prowadzi to do nieprzewidywalnych błędów.
3. Gradle: budowanie projektów wielomodułowych
Struktura projektu w 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 (główny)
subprojects {
apply plugin: 'java'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21) // lub twoja wersja Javy
}
}
repositories {
mavenCentral()
}
}
build.gradle dla core
plugins {
id 'java'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
build.gradle dla app
plugins {
id 'java'
id 'application'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
dependencies {
implementation project(':core')
}
application {
mainModule = 'app' // Nazwa twojego modułu Java
mainClass = 'com.example.app.Main'
}
Budowanie i uruchamianie
./gradlew build
./gradlew :app:run
Gradle automatycznie wykrywa module-path, jeśli znajdzie module-info.java. Jeśli coś nie działa — sprawdź, czy używasz aktualnej wersji Gradle i wtyczek.
4. IntelliJ IDEA: tworzenie i konfiguracja projektu modułowego
Tworzenie projektu modułowego
- File → New → Project → Java → włącz opcję „module-info.java”.
- Dla projektu wielomodułowego: File → New → Module — dodawaj nowe moduły do projektu.
- Jeśli używasz Maven/Gradle — po prostu zaimportuj projekt przez pom.xml lub build.gradle.
Struktura w IDEA
- W Project View każdy moduł jest wyświetlany jako oddzielna gałąź.
- W każdym module — własny module-info.java, własne źródła.
- IDEA podpowiada, które moduły eksportują/wymagają jakie pakiety (exports/requires).
- Jeśli zapomnisz wyeksportować pakiet lub zadeklarować zależność — IDEA podświetli błąd.
Sprawdzenie module-path, uruchomienie klasy main
- W Run/Debug Configurations podaj:
- Main class (np. com.example.app.Main)
- Module (np. app)
- IDEA sama zajmie się module-path.
- Jeśli uruchamiasz przez Maven/Gradle — użyj odpowiednich konfiguracji (np. app [run]).
Import zewnętrznych modułów i bibliotek
- Aby pracować z zewnętrznymi bibliotekami (np. requires java.sql; lub zewnętrzne pliki JAR) — dodaj zależność przez Maven/Gradle.
- Jeśli biblioteka nie jest modułowa (brak module-info.java), zostanie automatycznie dodana do „unnamed module” (to normalne dla większości bibliotek).
5. Praktyka: tworzenie prostego projektu wielomodułowego
Krok 1. Tworzymy strukturę projektu
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 "Witaj z modułu core!";
}
}
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;
}
Krok 2. Budowanie i uruchamianie przez Maven
W katalogu głównym projektu tworzymy parent pom.xml i dwa podprojekty (core/pom.xml, app/pom.xml) jak wyżej. W app/pom.xml dodajemy zależność od core.
mvn clean install
mvn -pl app exec:java
Krok 3. Budowanie i uruchamianie przez Gradle
Tworzymy settings.gradle, główny build.gradle oraz po jednym dla każdego modułu. W app/build.gradle dodajemy zależność od core i ustawiamy mainClass.
./gradlew build
./gradlew :app:run
Krok 4. Import i uruchamianie w IntelliJ IDEA
- Otwieramy katalog główny projektu przez File → Open.
- IDEA rozpozna strukturę Maven/Gradle i utworzy moduły.
- W każdym module można otworzyć module-info.java, IDEA pokaże powiązania między modułami.
- Uruchamiamy Main.java z menu kontekstowego („Run Main.main()”) — IDEA sama ustawi module-path.
6. Typowe błędy podczas budowania projektów modułowych
Błąd nr 1: Nieprawidłowy module-path. Jeśli podczas uruchamiania lub kompilacji pojawia się komunikat w rodzaju „module not found” lub „package is not visible”, najprawdopodobniej nie podałeś poprawnego module-path lub zapomniałeś dodać zależność w pom.xml/build.gradle.
Błąd nr 2: Duplikowanie nazw modułów. W projekcie nie powinno być dwóch modułów o tej samej nazwie (np. dwa module core). Prowadzi to do błędu kompilacji.
Błąd nr 3: Nieeksportowany pakiet. Jeśli klasa znajduje się w pakiecie, który nie jest eksportowany przez exports w module-info.java, to będzie on niedostępny dla innych modułów, nawet jeśli jest public. Pojawi się błąd kompilacji przy próbie użycia takiej klasy.
Błąd nr 4: Nie zadeklarowano requires. Jeśli moduł używa klas z innego modułu, ale nie wskazał requires w module-info.java, kompilator zgłosi błąd.
Błąd nr 5: Niezgodność z zewnętrznymi bibliotekami. Niedopasowanie modułów i bibliotek bez module-info.java (trafiają do „unnamed module”) zwykle nie przeszkadza, ale w niektórych konfiguracjach może być potrzebna dodatkowa konfiguracja.
Błąd nr 6: Uruchamianie z niewłaściwego modułu. W konfiguracji uruchamiania IDEA lub Maven/Gradle podano błędny main class lub moduł — aplikacja się nie uruchamia. Sprawdź Main, mainModule/module oraz używany profil.
GO TO FULL VERSION