CodeGym /Kurse /Spring Boot /Erster Arbeitskreis: Gradle und IDE

Erster Arbeitskreis: Gradle und IDE

Spring Boot
Level 3, Lektion 4
Verfügbar

1. Der erste Arbeitskreis als Fähigkeit

Die häufigste Anfänger-Falle klingt so: „Ich will schneller Controller schreiben, nicht eure Builds." Und das ist ein verständlicher Wunsch. Aber die Realität ist einfach: Ein Backend-Service lebt nicht nur im Code-Editor. Er lebt im Build, im Start, in der Reproduzierbarkeit der Umgebung und in der Fähigkeit, schnell zu verstehen, was passiert. Wenn man nicht in der Lage ist, ein Projekt sicher zu starten, wird jeder nächste Schritt zur Lotterie: Ob man im Code falsch liegt, ob die IDE „falsch konfiguriert ist", ob Gradle „beleidigt ist".

Zu diesem Zeitpunkt hat catalog-service aufgehört, ein einfach generiertes Zip-Archiv zu sein: Es hat verständliche Build-Regeln und eine lesbare Struktur. Jetzt ist es wichtig, einen ruhigen Arbeitskreis zu durchlaufen, damit das Projekt nicht nur gelesen, sondern sicher gestartet werden kann.

Wir können bereits build.gradle.kts lesen und uns im Projektgerüst orientieren. Jetzt braucht es den bodenständigsten Schritt: Das in einen wiederholbaren Arbeitskreis verwandeln, der gleichermaßen aus der IDE und aus dem Terminal funktioniert.

Deshalb fixieren wir den „Arbeitskreis" als Fähigkeit, nicht als zufälligen Haufen von Aktionen. Ein guter Arbeitskreis ist wie ein vertrauter Heimweg: Man wählt nicht jedes Mal neu, welche Straße man nimmt. Man kennt die Abfolge, und sie wiederholt sich. Für catalog-service sieht unser minimaler Kreislauf heute so aus: Projekt als Gradle-Modell importieren → Synchronisieren → Tasks ansehen → Dependencies ansehen → über bootRun starten. Das ist das Fundament, auf dem später alles andere wächst.

Wenn dieser Kreislauf ohne Überraschungen wiederholt wird, hören Dependencies und Starter auf, wie Magie zu wirken: Man weiß bereits, wo man sie sieht und womit man das Ergebnis prüft.

Den Prozess einmal als Schema zu sehen ist nützlich:

flowchart TD
    %% Der gesamte Kreislauf ist als Abfolge von Aktionen dargestellt, die man täglich wiederholt
    A["Import in IDE als Gradle-Projekt"] --> B["Gradle sync (Projektmodell aus build.gradle.kts)"]
    B --> C["./gradlew tasks (welche Operationen verfügbar sind)"]
    C --> D["./gradlew dependencies (welche Bibliotheken im Projekt sind)"]
    D --> E["./gradlew bootRun (Anwendung starten)"]
    E --> F["Anwendung stoppen (Ctrl+C)"]

2. catalog-service in die IDE importieren

Wenn das Projekt bereits generiert ist, möchte man schnell und einfach: Den Ordner öffnen, auf das grüne Dreieck neben main() drücken und es als Erfolg werten. Manchmal klappt das sogar… aber dieser Ansatz hat eine schlechte Gewohnheit: Er macht die IDE zur Hauptquelle der Wahrheit. Und unsere Hauptquelle der Wahrheit ist das Gradle-Modell, also build.gradle.kts + Wrapper + Toolchain-Einstellungen. Die IDE soll das Projekt aus diesem Modell lesen, nicht in einer parallelen Realität leben.

Stell dir vor, build.gradle.kts ist ein Rezept, und die IDE ist ein Küchenherd. Das Rezept sagt, was gekocht wird und in welchen Mengen. Wenn der Herd „ein eigenes Rezept erfunden hat", bekommst du vielleicht ein leckeres Gericht… oder unerwarteten Kompott aus Dependencies. Deshalb importieren wir das Projekt so, dass die IDE sieht: „Aha, das ist ein Gradle-Projekt, hier ist der Wrapper, hier sind Plugins, hier sind Tasks, hier sind Dependencies."

Da jeder eine andere IDE hat, beschreibe ich das Prinzip — das überall gleich ist — und die Details der Schaltflächen findest du leicht an den Wörtern Gradle und Import.

Du wählst den Projektimport als Gradle-Projekt, gibst den Projektstamm an — den Ordner, wo build.gradle.kts und gradlew liegen —, und stimmst zu, den Gradle Wrapper zu verwenden. Das ist ein wichtiger Moment: Die IDE soll den Build über gradlew starten, nicht über ein „eingebautes Gradle unbekannter Version". Dann wird die IDE vorschlagen, ein JDK für den Import zu wählen. Auch wenn in build.gradle.kts eine Toolchain konfiguriert ist, braucht die IDE trotzdem ein JDK, um Code zu analysieren und Gradle auszuführen. Normalerweise wählt man dasselbe wie in der Toolchain — in unserem Fall Java 25.

Um schnell zu prüfen, dass man den richtigen Stamm importiert hat, schau einfach auf die Projektstruktur. Im Stamm sollten mindestens diese Dateien sein:

catalog-service/
├── gradlew
├── gradlew.bat
├── build.gradle.kts
├── settings.gradle.kts
├── gradle/
│   └── wrapper/
│       └── gradle-wrapper.properties
└── src/
    ├── main/
    │   └── java/...
    └── test/
        └── java/...

Wenn man den falschen Ordner importiert hat (z. B. einen Ordner eine Ebene höher, wo andere Projekte liegen), beginnt die IDE seltsam zu verhalten: Tasks werden nicht gefunden, bootRun taucht nicht auf, Dependencies „werden nicht aufgelöst", und man beginnt Spring Boot wie eine Sekte zu verdächtigen. Nein, das ist einfach der falsche Projektstamm.

3. Wrapper und Java prüfen: ./gradlew --version

Nach dem Import möchte man sofort bootRun drücken, aber es ist nützlich, einen kleinen, fast rituellen Schritt zu machen: Sicherstellen, dass Gradle durch den Wrapper startet und ein normales Java sieht. Das ist wie vor einer Fahrt prüfen, ob das Auto Benzin hat und die Räder nicht viereckig sind. In der Entwicklungswelt spart ein solcher Check Stunden: Man versteht sofort, wo das Problem liegt — im Code oder in der Umgebung.

Der Prüfbefehl ist sehr einfach. Im Terminal öffnet man den Projektstamm (dort wo gradlew ist) und führt aus:

# Prüfen, ob Wrapper läuft und welche JVM Gradle sieht
./gradlew --version

Auf Windows kann man so rufen:

gradlew.bat --version

Die Ausgabe sieht ungefähr so aus (Zahlen können abweichen — das ist normal):

Gradle 9.4
------------------------------------------------------------
Build time:   ...
Kotlin:       ...
Groovy:       ...
Ant:          ...
JVM:          25 (Vendor ...)
OS:           ...

Hier sind nicht alle Zeilen wichtig, sondern zwei Fakten: Gradle startet, und die JVM ist ungefähr die erwartete (in unserem Baseline ist das Java 25). Wenn der Befehl funktioniert — super. Wenn der erste Start lang dauert, ist das auch normal: Gradle könnte die Distribution herunterladen, Dependencies auflösen und etwas in den Cache legen. Das ist kein „langsamer Computer", sondern das „Projekt erwacht zum ersten Mal."

Noch ein praktischer Aspekt: Wrapper-Befehle müssen immer aus dem Projektstamm gestartet werden. Wenn man versehentlich in src/main/java gelaufen ist, wird ./gradlew entweder nicht gefunden, oder Gradle sieht keinen Build-Kontext. In diesem Moment verkündet ein Anfänger feierlich: „Spring funktioniert bei mir nicht." In Wirklichkeit funktioniert einfach das aktuelle Verzeichnis nicht.

4. ./gradlew tasks: Karte der Projektmöglichkeiten

Wenn das Projekt importiert ist und Gradle startet, kommt die nächste sehr ingenieurtechnische Frage: „Was kann dieses Projekt überhaupt?" In Gradle formuliert man das als „welche Tasks (Aufgaben) sind verfügbar." Gute Nachricht: Man muss Befehle nicht auswendig kennen. Schlechte Nachricht: Es werden viele Tasks sein, und das sieht anfangs wie eine Liste von Zaubersprüchen aus.

Der Befehl, der die Task-Liste ausgibt, sieht so aus:

# Ansehen, welche Tasks im Projekt insgesamt verfügbar sind (einschließlich bootRun)
./gradlew tasks

Du bekommst eine große Ausgabe, in der Tasks normalerweise nach Bereichen gruppiert sind. Jetzt interessiert uns nicht der ganze Katalog, sondern einige Ankerpunkte, nach denen man sich im Projekt orientieren kann. Zum Beispiel siehst du fast sicher Tasks wie clean, build, test. Und wenn das Spring Boot Plugin eingebunden ist (bei uns ist es eingebunden), erscheinen auch Boot-Tasks, darunter bootRun.

Nützlicher Trick für Anfänger — nicht nur die Liste anschauen, sondern Gradle fragen: „Was genau macht dieser Task?" Dafür gibt es die Task-Hilfe:

# Beschreibung eines spezifischen Tasks abrufen: was er macht und welche Optionen er hat
./gradlew help --task bootRun

Die Ausgabe ist normalerweise kurz und verständlich: Beschreibung und Parameter. Das ist ein guter Weg, die Arbeit mit Gradle nicht zum Raten zu machen.

Um die „Task-Karte" im Kopf zu festigen, ist eine kleine Tabelle nützlich — nicht als „muss auswendig lernen", sondern als „wenn ich verloren bin, schaue ich hier nach":

Befehl Was er macht (auf Deutsch) Was du zu sehen erwartest
./gradlew tasks Gibt eine Liste verfügbarer Projektoperationen aus Große Task-Liste, darunter bootRun
./gradlew help --task bootRun Erklärt einen spezifischen Task Kurze Beschreibung, dass bootRun die Boot-Anwendung startet
./gradlew test Führt Tests aus (vorerst nur „Prüfung, dass alles lebt") Ergebnis BUILD SUCCESSFUL oder verständlicher Fehler

Und ein wichtiger Gedanke: Ein Gradle Task ist keine „einfache Kommandozeile." Es ist eine Projektoperation, die Abhängigkeiten kennt. Wenn z. B. für bootRun erst Code kompiliert werden muss, macht Gradle das selbst. Man muss nicht manuell „kompilieren, dann starten, dann noch etwas" ausführen. Darin liegt einer der Sinne eines Build-Tools.

5. ./gradlew dependencies: Dependency-Baum

Nach den Tasks ist es logisch, den zweiten großen Teil der Projektrealität zu betrachten: Dependencies. Einem Anfänger scheint oft: „Ich habe ja nur zwei Zeilen in dependencies." Aber durch Starter und transitive Dependencies kommen ziemlich viele Bibliotheken ins Projekt. Und das ist kein „Schrecken-Schrecken", sondern normales erwachsenes Leben eines Java-Service: Man bindet nicht ein Joghurt ein, sondern einen ganzen Zutaten-Satz.

Der einfachste Befehl:

# Dependency-Baum ausgeben (kann groß sein — das ist normal)
./gradlew dependencies

Er gibt einen Dependency-Baum aus. Und ja, er kann lang sein. Keine Panik nötig. In dieser Einheit analysieren wir keine Versionskonflikte — uns ist wichtig, die Dependency-Karte als Text sehen zu können, ohne IDE, weil das eine der universellsten Diagnosemethoden ist.

Um die Ausgabe leichter lesbar zu machen, ist es nützlich, eine spezifische Konfiguration anzugeben. Die praktischste für die Frage „was braucht die Anwendung wirklich zur Laufzeit" ist runtimeClasspath:

# Dependencies ansehen, die wirklich in den Anwendungs-Classpath zur Laufzeit kommen
./gradlew dependencies --configuration runtimeClasspath

Du siehst einen Baum, in dem irgendwo oben dein Starter steht, z. B. spring-boot-starter-webmvc (wenn du ihn gewählt hast), und darunter — ein ganzer Zweig von Bibliotheken. Das ist genau der Moment, wo man zu verstehen beginnt: Starter ist ein „Einstiegspunkt", kein „ein Jar."

Um das mit dem echten Projekt in Beziehung zu setzen, denk daran, dass die Dependency-Liste in build.gradle.kts „lebt." Ungefähr so (das ist kein neues Material, nur eine Anbindung an die Stelle in der Datei):

dependencies {
    // Dependencies für den Hauptcode der Anwendung
    implementation("org.springframework.boot:spring-boot-starter-webmvc")

    // Dependencies nur für Tests (landen nicht in Produktion)
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

Jetzt reichen zwei Fähigkeiten: Den Befehl starten können und sich mit dem Satz beruhigen können: „Lange Liste — das ist normal, das Projekt ist nicht kaputt." Wir werden heute nicht analysieren, warum genau solche Zweige da sind — das lässt sich leicht in „lass uns noch zwei Stunden den Baum anschauen" verwandeln.

6. ./gradlew bootRun: Starten und Stoppen

Hier sind wir beim Höhepunkt des Tages angelangt: Das Projekt so zu starten, dass es Teil des Build-Kreislaufs ist — nicht die „Stimmung deiner IDE." bootRun ist ein Gradle-Task vom Spring Boot Plugin, der die Anwendung direkt aus dem Projekt hochfährt. Aus Anfängersicht sieht das aus wie „Ich habe einen Befehl geschrieben — und ein Server ist gestartet." Aus Ingenieursicht — das ist ein Start, der den richtigen Classpath verwendet und ins Build-Modell eingebettet ist.

Aus dem Projektstamm starten:

# Anwendung über Gradle starten (richtiger Classpath und Build-Modell)
./gradlew bootRun

Der erste Start kann länger dauern: Gradle könnte Dependencies herunterladen, Klassen bauen, Caches aufwärmen. Das ist normal. In der Konsole siehst du das bekannte Boot-Bild: Banner, dann Log-Zeilen des Starts und irgendwann — eine Zeile, an der man erkennen kann, dass alles erfolgreich war.

Beispiel dessen, was man mit den Augen sucht (Formulierungen können abweichen, aber der Sinn ist einer):

Tomcat started on port 8080 (http)
Started CatalogServiceApplication in 1.2 seconds

Beachte: Wir analysieren jetzt nicht, „warum Tomcat" und „was genau beim Start passiert ist." In dieser Phase reicht uns eine einfache Bestätigung: Der Prozess lebt, die Anwendung ist nicht abgestürzt, der Port ist hochgefahren.

Wie prüft man, dass der Service wirklich auf dem Port lauscht? Der einfachste Weg — Browser öffnen und aufrufen:

http://localhost:8080/

Da wir noch keine Controller und Seiten geschrieben haben, wirst du höchstwahrscheinlich 404 sehen. Und das ist genau die gute Nachricht: Der Server ist hochgefahren und hat ehrlich gesagt „diese URL kenne ich nicht." Für die, die das Terminal bevorzugen, gibt es einen „Browser-freien Check":

 
  curl -i http://localhost:8080/
 

Die erwartete Antwort — Status 404, ungefähr so:

HTTP/1.1 404
...

Und noch eine wichtige Lebensfähigkeit: Wie man bootRun stoppt. Im Terminal macht man das klassisch:

 
  Ctrl + C
 

Wenn man die Anwendung gestoppt hat, wird Port 8080 freigegeben, und der nächste Start kann wieder hochfahren. Wenn man versehentlich einen Prozess laufen lässt und dann versucht, ein zweites Mal zu starten, kann man den Fehler „Port belegt" bekommen. Das ist kein „Spring ist kaputt", sondern einfach „du hast bereits einen laufenden Prozess."

7. IDE und Terminal: zwei Fernbedienungen für dasselbe Projekt

In einer idealen Welt konkurrieren Terminal und IDE nicht, sondern ergänzen sich. Die IDE ist bequem, weil sie Highlighting, Code-Navigation, Start-Buttons zeigt und das Gefühl der Kontrolle erzeugt. Das Terminal ist bequem, weil es ehrlich und gleich auf jedem Rechner, in jeder IDE und in jeder Umgebung funktioniert. Und wenn man gelernt hat, Basisaktionen über ./gradlew zu machen, hört man auf, Geisel eines bestimmten Buttons zu sein.

Praktisch bedeutet das: Selbst wenn man bootRun aus der IDE über das Gradle-Tool-Window startet, ist es nützlich zu wissen, dass das im Terminal ein Befehl ist. Und es ist nützlich zu verstehen: Wenn die IDE „bei der Synchronisierung hängt", kann man im Terminal ./gradlew tasks ausführen und sehen, dass das Projekt grundsätzlich lebendig ist — die IDE verdaut gerade nur das Modell.

Noch ein ruhiges Kriterium „alles in Ordnung": Wenn man dieselbe Aktion in der IDE und im Terminal macht, sollte das Ergebnis gleich sein. Wenn das Projekt aus der IDE startet, aber aus dem Terminal nicht (oder umgekehrt), ist das fast immer ein Signal für verschiedene JDKs, verschiedene Arbeitsverzeichnisse, verschiedene Gradle-Einstellungen (z. B. verwendet die IDE nicht den Wrapper). Und das ist genau der Ort, wo man kein Anfänger „auf Glück" mehr ist, sondern jemand, der Diskrepanzen diagnostizieren kann.

Für einen kleinen „Spickzettel in der Hand" — er wäre fast lächerlich kurz:

Tasks ansehen:          ./gradlew tasks
Dependencies ansehen:   ./gradlew dependencies
Anwendung starten:      ./gradlew bootRun

Das ist der minimale Arbeitskreis, der nicht von der Stimmung deiner IDE abhängt.

8. Typische Fehler beim Arbeiten mit Gradle und IDE

Fehler Nr. 1: Projekt als „Ordner" geöffnet, nicht als Gradle-Projekt importiert.
Das äußert sich sehr unterschiedlich: Bei manchen sieht die IDE Dependencies nicht, bei manchen gibt es kein Gradle-Tool-Window, bei manchen ist Code „rot" und Spring-Klassen fehlen. Oft sieht es so aus, als würde das Projekt „nicht kompilieren", obwohl ./gradlew bootRun im Terminal funktioniert. Die Lösung ist einfach: Projekt genau als Gradle-Projekt importieren und die Verwendung des Wrappers aktivieren.

Fehler Nr. 2: Befehle werden nicht aus dem Projektstamm gestartet.
Man geht ins Terminal, tippt automatisch ./gradlew bootRun — und bekommt „No such file or directory" oder Gradle beschwert sich, dass es build.gradle.kts nicht sieht. Das ist kein Rätsel und kein Fluch, sondern ein normales Dateisystem. Prüfe, ob du im Ordner bist, wo gradlew und build.gradle.kts liegen.

Fehler Nr. 3: Globales Gradle statt Wrapper wird gestartet.
Manchmal ist Gradle im System installiert, und die Hand greift nach gradle bootRun. Das kann funktionieren… bis zur ersten Versions- oder Einstellungs-Diskrepanz. Im Lern- und Team-Projekt ist es besser, sofort die Gewohnheit zu entwickeln: Immer ./gradlew .... Wrapper ist ein Weg zu sagen: „Wir bauen mit diesem Werkzeug und dieser Version" — nicht „was auch immer bei wem installiert ist."

Fehler Nr. 4: Java in IDE und Java in Gradle Toolchain passen nicht zusammen.
Der Build über das Terminal kann das richtige Java (Toolchain) verwenden, aber die IDE analysiert das Projekt mit einer anderen Version und beschwert sich mit seltsamen Fehlern. Oder umgekehrt: IDE startet auf einem JDK, Terminal auf einem anderen, und das Verhalten divergiert. Erkennbar an einfachen Symptomen: „In der IDE funktioniert es, aber ./gradlew nicht" oder umgekehrt. Beginne die Diagnose mit ./gradlew --version und den JDK-Einstellungen in der IDE.

Fehler Nr. 5: Erster Start „hängt lang", und es scheint, als wäre alles kaputt.
Beim ersten Start lädt Gradle Dependencies herunter — das kann Minuten dauern (besonders bei langsamer Verbindung). In diesem Moment sind keine abrupten Bewegungen angebracht wie „lösche den Ordner .gradle", „regeneriere das Projekt", „installiere Java neu." Lass den Prozess fertig werden. Wenn das Herunterladen wirklich hängt, siehst du zumindest, bei welchem Schritt es steckt, und kannst ruhig analysieren.

1
Aufgabe
Spring Boot, Level 3, Lektion 4
Gesperrt
Erster vollständiger Zyklus über die Befehlszeile
Erster vollständiger Zyklus über die Befehlszeile
1
Aufgabe
Spring Boot, Level 3, Lektion 4
Gesperrt
Start-Log und Hilfe zu `bootRun`
Start-Log und Hilfe zu `bootRun`
1
Umfrage/Quiz
Spring Gradle, Level 3, Lektion 4
Nicht verfügbar
Spring Gradle
Projekt erstellen und starten
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION