CodeGym /Kurse /Java Server /build/, jar und Start außerhalb der IDE

build/, jar und Start außerhalb der IDE

Java Server
Level 4, Lektion 3
Verfügbar

1. Start außerhalb der IDE

Zu diesem Zeitpunkt ist run bereits konfiguriert: Gradle weiß, welches main() gestartet werden soll und wie der Classpath dafür aufgebaut wird. Aber damit endet die Geschichte des Builds nicht. Nach classes, jar und run hinterlässt das Projekt ganz konkrete Spuren: kompilierte Klassen, verarbeitete Ressourcen und ein Archiv in build/libs.

Deshalb schauen wir jetzt nicht auf den Startvorgang selbst, sondern auf die Spuren des Builds. Wo liegen diese Dateien, was genau landet in einem normalen jar und worin unterscheidet sich dieses Archiv vom Start über Gradle — das ist es, was wir verstehen wollen.

Der Befehl ./gradlew run gibt bereits einen reproduzierbaren Start. Jetzt nutzen wir ihn als Vergleichspunkt: Er hilft zu sehen, was Gradle automatisch zusammenstellt und was man beim direkten JVM-Start selbst angeben muss.

2. Inhalt von build/ nach ./gradlew run

Der Ordner build/ ist der Ort, wo Gradle die Ergebnisse seiner Arbeit ablegt. Stell dir vor, src/ sind „Zutaten und Rezept", und build/ ist „die Küche nach dem Kochen": dort liegen vorbereitete Dateien, Zwischenergebnisse und fertige Artefakte. Wichtig: Die Küche kann chaotisch sein, aber das Rezept sollte darunter nicht leiden.

Wenn du ./gradlew run ausführst, kompiliert Gradle zunächst den Code und verarbeitet die Ressourcen (falls nötig) und startet dann die Anwendung. Die Ergebnisse dieser Vorbereitung siehst du genau in build/.

Ungefähr so (vereinfacht) sieht aus, was man „auf Anhieb erkennen" können sollte:

build
├─ classes/java/main        <- kompilierte .class-Dateien
├─ resources/main           <- verarbeitete Ressourcen
└─ libs                     <- hier landet normalerweise das Jar

Eine nützliche Gewohnheit: Versuche nicht, den Build „nach Gefühl zu verstehen" — überprüfe die Spuren in build/. Wenn du classes ausgeführt hast, aber build/classes/java/main nicht existiert, bedeutet das, dass die Aufgabe nicht abgeschlossen wurde. Wenn Ressourcen in src/main/resources liegen, aber nicht in build/resources/main auftauchen, liegt irgendwo bei der Ressourcenverarbeitung ein Problem.

Um nichts im Kopf behalten zu müssen, ist eine kleine Tabelle praktisch:

Was wir suchen Wo es normalerweise liegt Woher es kommt Was das bedeutet
Kompilierte Klassen build/classes/java/main nach compileJava (innerhalb von classes) Java-Code wurde erfolgreich kompiliert
Ressourcen build/resources/main nach processResources (innerhalb von classes) Dateien aus src/main/resources sind im Classpath beim Start gelandet
Anwendungsarchiv build/libs/*.jar nach jar das Projekt wurde in eine .jar-Datei gepackt

Und noch ein Prinzip, das oft Nerven spart: Der Ordner build/ ist keine Quelle der Wahrheit. Wenn du eine Datei in build/resources/main bearbeitet hast, geht das beim nächsten Build fast sicher verloren. Geändert wird nur in den Quelldateien in src/, und build/ betrachtet man als einmaliges Ergebnis.

3. Jar bauen: Datei und Speicherort

Das Wort jar erschreckt nur die ersten zwei Male. In Wirklichkeit ist es einfach ein Archiv (im Grunde eine Zip-Datei), in dem das Ergebnis deines Projekts steckt. Aber hier ist eine wichtige Grenze zu ziehen: Das normale jar, das Gradle mit dieser Aufgabe baut, ist eine Zusammenstellung deines Codes, der Ressourcen und der Servicemetadaten. Es muss kein eigenständiges Archiv mit allen externen Bibliotheken sein.

Das ist eine Möglichkeit zu sagen: „Hier ist das Build-Ergebnis verpackt, man kann es als Datei weitergeben."

Ein jar in einem Gradle-Projekt baut man mit folgendem Befehl:

./gradlew jar

Danach schau in den Ordner build/libs. Dort erscheint eine .jar-Datei. Ihr Name hängt normalerweise von den Projekteinstellungen ab und sieht oft so aus: projektname-version.jar. Wenn noch keine Version definiert wurde, kann Gradle etwas wie unspecified einsetzen — das ist kein Fehler, die Version ist einfach noch nicht festgelegt.

Um nicht mit der Realität zu diskutieren, kann man es so machen: erst bauen, dann schauen, was entstanden ist:

# Jar-Archiv der Anwendung bauen
./gradlew jar
# Prüfen, welche Datei erschienen ist
ls build/libs

(Unter Windows verwendet man statt ls normalerweise dir — der Sinn ist derselbe: den Dateinamen sehen.)

Und hier ist es wichtig, zwei verschiedene Bedeutungen nicht zu verwechseln:

  • ./gradlew run — das ist das Starten der Anwendung über Gradle (mit dem richtigen Classpath und den Abhängigkeiten).
  • ./gradlew jar — das ist das Verpacken des Ergebnisses in ein Archiv. Das ist kein „weiterer run", sondern ein anderes Build-Produkt.

4. Inhalt des Jars und Ressourcen prüfen

Wenn man zum ersten Mal ein jar baut, stellt sich die natürliche Frage: „Was ist darin? Habe ich wirklich das Richtige gebaut?" Gute Neuigkeit: Das lässt sich ohne Raten prüfen.

Es gibt einen einfachen Befehl, der den Inhalt des Archivs anzeigt:

# Inhalt des Jar-Archivs anzeigen (eigenen Dateinamen einsetzen)
jar tf build/libs/readlater-starter.jar

Der Dateiname bei dir kann abweichen, also setze deinen aus build/libs ein. Der Befehl jar tf gibt eine Liste der Dateien im Archiv aus. Dort solltest du mindestens sehen:

  • deine Klasse ReadLaterApplication als .class-Datei;
  • deine Ressourcen (zum Beispiel banner.txt, wenn du sie in src/main/resources hinzugefügt hast).

Das sieht ungefähr so aus (Auszug):

META-INF/
META-INF/MANIFEST.MF
com/example/readlater/ReadLaterApplication.class
banner.txt

Wenn du banner.txt im Jar siehst, hast du einen sehr wichtigen Moment erwischt: Die Ressource ist Teil des Artefakts geworden. Das heißt, die Anwendung kann diese Datei nicht lesen, weil irgendwo ein src/-Ordner liegt, sondern weil die Ressource ins Endergebnis verpackt wurde und über den Classpath zugänglich ist.

Aus Code-Sicht ist hier ein Aufruf wichtig, der bereits in ReadLaterApplication lebt:

ReadLaterApplication.getResourceAsStream("/banner.txt")

Für die JVM spielt es keine große Rolle, ob banner.txt während ./gradlew run in build/resources/main liegt oder nach der Verpackung schon im jar. In beiden Fällen greift der Code auf die Ressource über den Classpath zu, nicht über den Pfad src/main/resources/....

5. Start ohne IDE: Gradle run und java -cp

Jetzt unterscheiden wir zwei ähnliche, aber verschiedene Dinge.

./gradlew run — das ist der Start in der Entwicklung. Gradle stellt selbst den Classpath zusammen, greift die Ressourcen auf, fügt Laufzeitabhängigkeiten hinzu und ruft mainClass auf.

java -cp ... — das ist der direkte JVM-Start. Hier gibst du den Classpath selbst an, und die JVM weiß nur, was du ihr explizit übergeben hast.

Diesen direkten Start zeigt man am besten im kleinsten Szenario, wo die Anwendung mit eigenem Code und der Ressource banner.txt auskommt. Sobald externe Bibliotheken ins Spiel kommen, reicht das normale jar aus build/libs nicht mehr: Die JVM muss auch diese Abhängigkeiten sehen.

Über Gradle sieht der Start so aus:

./gradlew run

Und direkt über die JVM — so:

# Hauptklasse direkt aus dem Jar über Classpath starten
java -cp build/libs/readlater-starter.jar com.example.readlater.ReadLaterApplication

Bei diesem minimalen Beispiel können beide Varianten das gleiche Ergebnis liefern. Aber die Grenze zwischen ihnen ist wichtig: ./gradlew run stellt den nötigen Classpath automatisch zusammen, und beim direkten Start liegt die Verantwortung für den Classpath bei dir.

Dasselbe gilt für Start-Argumente. Über Gradle:

./gradlew run --args="hello world"

Und direkt über die JVM:

java -cp build/libs/readlater-starter.jar com.example.readlater.ReadLaterApplication hello world

In beiden Fällen landen die Argumente im selben String[] args. Sobald der Unterschied zwischen run, normalem jar und direktem JVM-Start klar ist, sieht man viel leichter, wo genau der Build gebrochen ist: bei der Artefaktverpackung, im Classpath oder schon im Code selbst.

6. Typische Fehler beim Arbeiten mit build/ und jar

Fehler Nr. 1: Dateien in build/ bearbeiten, weil „das schneller ist".
Das ist sehr verlockend: Du hast build/resources/main/banner.txt gefunden, den Text geändert — und er hat sich sofort geändert. Dann führst du ./gradlew clean aus oder einfach den nächsten Build, und die Änderungen verschwinden. Der Grund ist einfach: build/ sind keine Quelldateien, sondern ein Ergebnis. Die Lösung ist die Gewohnheit, nur src/main/java und src/main/resources zu bearbeiten.

Fehler Nr. 2: Denken, dass jar „ein Start" ist und run „auch ein Start, nur anders".
In der Praxis sind das zwei verschiedene Ziele. run — zum bequemen Starten der Anwendung in der Entwicklung. jar — zum Verpacken des Ergebnisses. Wenn man die Frage „Möchte ich gerade starten oder ein Artefakt bekommen?" im Kopf hat, verschwindet die Verwechslung fast sofort.

Fehler Nr. 3: Überrascht sein, dass jar keine Abhängigkeiten enthält, und von ihm das Verhalten „einer einzigen Datei für alles" erwarten.
Das ist ein normaler Einsteiger-Schock: „Ich habe doch Bibliotheken in Gradle eingebunden, warum sind sie nicht im Jar?" Weil das normale jar eine Verpackung deines Codes und deiner Ressourcen ist. Dass Abhängigkeiten bei run hinzugefügt werden, ist das Verdienst von Gradle, das den Classpath zusammenstellt. Für den Moment ist es wichtig, das Grundbild zu fixieren: jar ist keine Wunderkiste, sondern ein konkretes Format.

Fehler Nr. 4: Artefakte nicht da suchen, wo sie wirklich liegen.
Wenn der Build durchgelaufen ist, man aber das jar „nicht sieht", liegt es meist nicht an Magie, sondern daran, dass man nicht in build/libs schaut. Genauso mit Klassen und Ressourcen: Wenn man nicht versteht, ob sie erschienen sind, prüfe build/classes/java/main und build/resources/main. Das ist banal, spart aber Stunden.

Fehler Nr. 5: Sich selbst beweisen wollen, dass alles reproduzierbar ist, aber nur durch Start aus der IDE.
Die IDE ist ein tolles Werkzeug, aber ihre Konfiguration lebt auf deiner Maschine. Reproduzierbarkeit prüft man mit einer einfachen Sache: „Ich schließe die IDE, öffne ein Terminal, führe ./gradlew run aus — und das Projekt startet." Wenn das funktioniert, ist das Projekt wirklich reproduzierbar, und nicht „funktioniert nur beim Autor auf dem Laptop".

1
Aufgabe
Java Server, Level 4, Lektion 3
Gesperrt
Build-Spuren im build/-Verzeichnis und Inhalt der Jar-Datei
Build-Spuren im build/-Verzeichnis und Inhalt der Jar-Datei
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION