AB 01 – Das Projekt aufsetzen und den ersten Endpunkt bauen
Die Situation
Die Personalabteilung der HiTec GmbH verwaltet ihre Mitarbeiterdaten bislang in einer Tabellenkalkulation. Künftig sollen mehrere Programme darauf zugreifen: die neue Weboberfläche, eine App für das Außendienstteam und die Zeiterfassung an der Pforte.
Dein Auftrag: Baue ein Backend, das die Personendaten verwaltet und sie allen drei Programmen über eine einheitliche Schnittstelle zur Verfügung stellt.
In diesem ersten Arbeitsblatt entsteht noch keine Datenverwaltung. Es geht darum, das Projekt zum Laufen zu bringen und den ersten Endpunkt zu bauen — damit du siehst, dass die Grundlage steht.
Lernziele
Nach Bearbeitung dieses Arbeitsblatts kannst du:
- ein Spring-Boot-Projekt mit dem Spring Initializr erzeugen und die benötigten Abhängigkeiten auswählen
- den Aufbau eines Spring-Boot-Projekts erklären (
pom.xml,application.properties, Application-Klasse) - einen REST-Controller mit einem
GET-Endpunkt implementieren - einen Record als Datenklasse einsetzen und erklären, warum er sich für Antwortobjekte eignet
- den Zustand deiner laufenden Anwendung über den Actuator prüfen
Lies vorher die Infoblätter Was ist ein Webservice? und Das REST-Paradigma. Für den Aufbau der Nachrichten hilft HTTP kompakt.
Warum ein Spring-Boot-Projekt kein normales Java-Projekt ist
Ein Webservice muss über das Netzwerk erreichbar sein. Dafür braucht es einen Webserver. Früher hat man den getrennt installiert und die eigene Anwendung dort hineingelegt.
Spring Boot dreht das um: Der Webserver wird Teil der Anwendung. Am Ende entsteht eine einzige .jar-Datei, die alles enthält, was sie zum Laufen braucht — man startet sie wie ein gewöhnliches Java-Programm.
Das ist der Grund, warum solche Anwendungen sich gut in Container packen und in der Cloud betreiben lassen. Und es ist der Grund, warum ein Spring-Boot-Projekt nicht von Hand angelegt, sondern über den Spring Initializr erzeugt wird.
Aufgaben
Aufgabe 1 – Projekt in IntelliJ erzeugen
IntelliJ bringt den Spring Initializr eingebaut mit. Du musst also nichts herunterladen und nichts entpacken — die Entwicklungsumgebung erledigt das.
Schritt 1: Den Assistenten öffnen
Schritt 2: Den Generator „Spring Boot" wählen
Der Assistent sieht diesmal anders aus als gewohnt: Links stehen nicht nur die üblichen Projektarten, sondern darunter ein zweiter Abschnitt Generators.
Ganz oben in derselben Liste steht unter New Project auch ein Eintrag Java. Der erzeugt ein gewöhnliches Java-Projekt ohne Spring Boot — damit läuft später nichts von dem, was in diesen Arbeitsblättern steht.
Du brauchst den Eintrag weiter unten im Abschnitt Generators.
Schritt 3: Die Projektangaben eintragen

Sie zeigt den Dialog vor dem Ausfüllen — die Felder stehen noch auf den Voreinstellungen. Auch der Ordner unter Location ist nur ein Beispiel; nimm deinen eigenen.
Maßgeblich sind die Werte aus der folgenden Tabelle.
| Feld | Wert | Hinweis |
|---|---|---|
| Name | personenverwaltung | Der Projektname |
| Location | z. B. C:\projekte | Der Ordner, in dem das Projekt angelegt wird |
| Language | Java | ist meist schon gewählt |
| Type | Maven | ⚠️ Vorgabe ist Gradle - Groovy — unbedingt umstellen |
| Group | de.szut | |
| Artifact | personenverwaltung | |
| Package name | de.szut.personenverwaltung | füllt sich meist automatisch |
| JDK | Version 26 | |
| Java | 26 | ⚠️ Vorgabe ist oft 17 |
| Packaging | Jar |
Type: IntelliJ schlägt Gradle - Groovy vor. Wir arbeiten mit Maven — sonst passt später nichts zusammen, was in den Arbeitsblättern steht.
Java-Version: Die Vorgabe ist häufig 17. Stelle beides auf 26 — sowohl das JDK als auch das Feld Java.
Pfade mit Umlauten: Ein Ort wie C:\Schüler\Übungen\ führt bei manchen Werkzeugen zu schwer auffindbaren Fehlern. Nimm etwas wie C:\projekte.
Schritt 4: Die Abhängigkeiten auswählen
Auf der zweiten Seite des Assistenten wählst du aus, welche Bausteine dein Projekt bekommen soll.

Die Seite ist dreigeteilt: Oben die Spring-Boot-Version, in der Mitte die nach Kategorien geordneten Abhängigkeiten, rechts oben die Beschreibung des gerade markierten Eintrags und rechts unten die Liste Added dependencies mit dem, was du bereits gewählt hast.
Versionen mit diesem Zusatz sind unfertige Entwicklungsstände, die sich täglich ändern können. Nimm immer eine Version ohne diesen Zusatz.
Spring Boot erscheint mehrmals im Jahr neu. In den Screenshots steht deshalb möglicherweise eine kleinere Versionsnummer als die, die dir angeboten wird. Nimm die neueste 4.1.x aus der Liste — die Arbeitsblätter passen auf jede davon.
| Abhängigkeit | Kategorie | Wofür |
|---|---|---|
| Spring Web | Web | Damit die Anwendung HTTP versteht und REST-Endpunkte anbieten kann |
| Spring Data JPA | SQL | Für den Datenbankzugriff, ohne SQL selbst zu schreiben |
| H2 Database | SQL | Eine Datenbank, die keine Installation braucht |
| Spring Boot Actuator | Ops | Liefert Betriebsinformationen über die laufende Anwendung |
Die Kategorien lassen sich über den Pfeil links aufklappen. Schneller geht es über das Suchfeld:

Bei der Suche nach Spring Web erscheinen drei ähnliche Einträge:
- Spring Web ← dieser ist gemeint
- Spring Reactive Web — eine andere Programmierweise, passt nicht zu diesem Tutorial
- Spring Web Services — für SOAP, nicht für REST
Verwechselst du sie, kompiliert später nichts von dem, was in den Arbeitsblättern steht.
Rechts unter Added dependencies müssen am Ende genau vier Einträge stehen. Steht dort noch No dependencies added, hat das Anhaken nicht funktioniert — klicke direkt auf das kleine Kästchen vor dem Namen.
Maven lädt jetzt über hundert Dateien aus dem Internet. Beim ersten Mal dauert das mehrere Minuten, danach nur noch Sekunden. Warte ab, bis die Fortschrittsanzeige unten verschwunden ist.
Warum aus vier Einträgen über hundert Dateien werden, steht im Infoblatt Maven und Abhängigkeiten.
Alternative: über start.spring.io im Browser
Wenn du eine andere Entwicklungsumgebung benutzt (VS Code, Eclipse) oder das Projekt lieber im Browser erzeugst
Der Spring Initializr ist ein Webdienst. IntelliJ benutzt im Hintergrund genau diesen Dienst — du kannst ihn aber auch direkt aufrufen. Das Ergebnis ist dasselbe.
1. Öffne start.spring.io im Browser.
2. Stelle die folgenden Werte ein:
| Feld | Wert |
|---|---|
| Project | Maven |
| Language | Java |
| Spring Boot | die neueste 4.1.x (ohne den Zusatz SNAPSHOT) |
| Group | de.szut |
| Artifact | personenverwaltung |
| Name | personenverwaltung |
| Package name | de.szut.personenverwaltung |
| Packaging | Jar |
| Java | 26 |
3. Füge über ADD DEPENDENCIES dieselben vier Abhängigkeiten hinzu: Spring Web, Spring Data JPA, H2 Database, Spring Boot Actuator.

4. Klicke auf GENERATE. Entpacke die heruntergeladene ZIP-Datei an einen Ort ohne Leerzeichen und Umlaute im Pfad.
5. Öffne den entpackten Ordner anschließend in deiner Entwicklungsumgebung.
Aufgabe 2 – Projekt öffnen und starten
Hast du den Weg über IntelliJ genommen, ist das Projekt bereits offen — dann übersprings die nächste Aufgabe und lies gleich weiter.
Sieh dir an, was der Initializr erzeugt hat:
personenverwaltung/
├── pom.xml ← Projektbeschreibung: Abhängigkeiten, Java-Version
├── mvnw / mvnw.cmd ← der Maven Wrapper
└── src/
├── main/
│ ├── java/de/szut/personenverwaltung/
│ │ └── PersonenverwaltungApplication.java ← Startpunkt
│ └── resources/
│ └── application.properties ← Einstellungen
└── test/
└── java/… ← Tests
mvnw (Linux/macOS) bzw. mvnw.cmd (Windows) lädt automatisch die Maven-Version, die zum Projekt gehört. Dadurch bauen alle im Team mit derselben Version — unabhängig davon, was auf dem einzelnen Rechner installiert ist.
In Betrieben gilt: Nutze den Wrapper, nicht dein lokales Maven.
Was Maven sonst noch tut, woher die Bibliotheken kommen und warum aus deinen vier ausgewählten Abhängigkeiten über hundert Dateien werden, steht im Infoblatt Maven und Abhängigkeiten.
In der Konsole erscheint zum Schluss eine Zeile wie:
Tomcat started on port 8080 (http) with context path '/'
Started PersonenverwaltungApplication in 2.417 seconds
Tomcat ist der eingebaute Webserver. Deine Anwendung ist jetzt unter http://localhost:8080 erreichbar.
Aufgabe 3 – Prüfen, ob die Anwendung wirklich läuft
Die Anwendung bietet noch keinen einzigen eigenen Endpunkt an. Der Actuator liefert aber bereits einen.
Du siehst etwas wie:
{"groups":["liveness","readiness"],"status":"UP"}
Entscheidend ist "status":"UP". Das Feld groups nennt zusätzliche Prüfgruppen, die der Actuator mitbringt — je nach Version kann die Antwort deshalb etwas anders aussehen.
/actuator/health liefert 200 OK, /gibtesnicht liefert 404 Not Found. Der Browser zeigt beide Male irgendetwas an — der Unterschied steckt im Statuscode, nicht im sichtbaren Text.
Das ist der Grund, warum man bei Webservices immer auf den Statuscode schaut.
Aufgabe 4 – Der erste eigene Endpunkt
Jetzt baust du einen Endpunkt, der einen Begrüßungstext zurückgibt. Er braucht noch keine Datenbank.
Beides. Ein Package ist ein Ordner, mehr steckt physisch nicht dahinter: keine Konfigurationsdatei, kein Eintrag irgendwo. Zum Package wird der Ordner erst durch die erste Zeile der Dateien darin:
package de.szut.personenverwaltung.controller;
Ordner und diese Zeile müssen zueinander passen — das ist die ganze Regel.
Und wo liegt dieser Ordner? Nicht im Projektverzeichnis. Packages beginnen
erst unterhalb von src/main/java:
personenverwaltung/ ← das Projektverzeichnis
├── pom.xml hier liegen KEINE Packages
├── mvnw
└── src/
├── main/
│ ├── java/
│ │ └── de/szut/personenverwaltung/ ← ab hier beginnen die Packages
│ │ ├── PersonenverwaltungApplication.java
│ │ └── controller/ ← dieses legst du gleich an
│ └── resources/
│ └── application.properties
└── test/
Die drei Ordner de, szut und personenverwaltung sind übrigens auch schon
Packages — sie entsprechen den drei Teilen von de.szut.personenverwaltung.
Spring Initializr hat sie beim Erzeugen des Projekts angelegt.
In IntelliJ gibt es zwei Wege dorthin:
| Weg | Was du eingibst |
|---|---|
Rechtsklick unterhalb von src/main/java → New → Package | den vollen Namen mit Punkten: de.szut.personenverwaltung.controller — IntelliJ legt daraus die verschachtelten Ordner an |
Rechtsklick auf den Ordner personenverwaltung → New → Directory | nur controller, ohne Punkte |
Findest du den Eintrag Package nicht, hast du außerhalb des Quelltextordners geklickt. Schlimm ist das nicht — nimm Directory.
Die häufigste Panne: einen Namen mit Punkten in New → Directory
eintippen. Dann entsteht ein Ordner, der wörtlich
de.szut.personenverwaltung.controller heißt, und keine Klasse darin lässt sich
übersetzen. Punkte gehören in New → Package, ein einzelner Name in beides.
Und wenn du dir das sparen willst: Du kannst den Ordner ganz weglassen und
gleich die Klasse anlegen. Gib bei New → Java Class als Namen
controller.Greeting ein — IntelliJ legt den Ordner mit an und schreibt die
package-Zeile selbst.
package de.szut.personenverwaltung.controller;
import java.time.LocalDateTime;
public record Greeting(String message, LocalDateTime time) {
}
Das ist die vollständige Klasse. Ein Record erzeugt automatisch Konstruktor, Zugriffsmethoden, equals(), hashCode() und toString().
Ein Record ist unveränderlich: Einmal erzeugt, lässt sich sein Inhalt nicht mehr ändern.
Genau das will man bei einem Antwortobjekt. Es wird gebaut, verschickt — fertig. Niemand soll es unterwegs verändern. Die Sprache erzwingt hier, was die Architektur ohnehin verlangt.
Später wirst du sehen, dass Datenbankklassen aus genau dem umgekehrten Grund keine Records sein können.
package de.szut.personenverwaltung.controller;
import java.time.LocalDateTime;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1/welcome")
public class WelcomeController {
@GetMapping
public Greeting welcome() {
return new Greeting("Willkommen bei der Personenverwaltung!", LocalDateTime.now());
}
}
Was die drei Annotationen bedeuten:
| Annotation | Bedeutung |
|---|---|
@RestController | Sagt Spring: Diese Klasse bietet REST-Endpunkte an. Rückgabewerte werden automatisch in JSON umgewandelt. |
@RequestMapping("/api/v1/welcome") | Der gemeinsame Pfadanfang aller Endpunkte dieser Klasse. |
@GetMapping | Diese Methode beantwortet GET-Anfragen an diesen Pfad. |

Du hast ein Java-Objekt zurückgegeben — und im Browser steht JSON. Diese Umwandlung nennt man Serialisierung. Spring erledigt sie automatisch. Aus den Record-Komponenten message und time werden die JSON-Felder gleichen Namens.
Wie JSON aufgebaut ist und wie Java-Typen darauf abgebildet werden, steht im Infoblatt JSON.
Aufgabe 5 – Testfälle ausführen
Ab jetzt prüfst du deine Arbeit systematisch. Ein Testfall beschreibt, was du tust und was dabei herauskommen muss — unabhängig davon, wer ihn ausführt.
| ID | Beschreibung | Vorbedingung | Testschritte | Erwartetes Ergebnis | Ergebnis |
|---|---|---|---|---|---|
| TF-01 | Anwendung startet | Projekt ist geöffnet, Port 8080 ist frei |
| In der Konsole erscheint Started PersonenverwaltungApplication. Es wird keine Ausnahme geworfen. | |
| TF-02 | Health-Endpunkt erreichbar | Anwendung läuft |
| Status 200, die Antwort enthält "status":"UP" | |
| TF-03 | Welcome-Endpunkt liefert JSON | Anwendung läuft |
| Status 200. Antwort enthält die Felder message und time. | |
| TF-04 | Unbekannter Pfad wird abgewiesen | Anwendung läuft |
| Status 404 — nicht 200 und nicht 500. | |
| TF-05 | Die Antwort ist als JSON gekennzeichnet | Anwendung läuft |
| Unter den Antwort-Headern steht Content-Type: application/json. |
Was passiert bei richtiger Adresse, aber falscher Methode? Ein POST auf /api/v1/welcome wird mit 405 Method Not Allowed beantwortet — nicht mit 404, denn die Adresse gibt es ja. Nur die Methode passt nicht zu @GetMapping.
Ausprobieren kannst du das jetzt noch nicht: Über die Adresszeile des Browsers geht ausschließlich GET. Für alle anderen Methoden brauchst du ein Werkzeug — das bekommst du im nächsten Arbeitsblatt. Der zugehörige Testfall steht dann in Arbeitsblatt 03.
Solange du selbst entwickelst, hast du im Kopf, was funktionieren soll. Sobald jemand anderes prüft — oder du selbst in drei Wochen — ist dieses Wissen weg.
Ein guter Testfall nennt neben seiner Bezeichnung drei Dinge: die Vorbedingung, die Schritte und das erwartete Ergebnis. Er ist so genau, dass zwei Personen unabhängig voneinander zum selben Urteil kommen.
Zusammenfassung
- Ein Spring-Boot-Projekt entsteht über den Spring Initializr und bringt seinen Webserver selbst mit.
- Der Maven Wrapper (
mvnw) sorgt dafür, dass alle mit derselben Maven-Version bauen. @RestControllerund@GetMappingmachen aus einer Java-Methode einen erreichbaren Endpunkt.- Ein Record ist eine unveränderliche Datenklasse — ideal für Antwortobjekte.
- Spring wandelt Rückgabeobjekte automatisch in JSON um (Serialisierung).
- Der Statuscode sagt mehr über den Erfolg einer Anfrage aus als der sichtbare Text.
Selbstkontrolle
Beantworte die Fragen, bevor du nachsiehst.
- Warum wird ein Spring-Boot-Projekt nicht als gewöhnliches Java-Projekt angelegt?
- Wozu dient die Annotation
@RestController? - Was ist der Unterschied zwischen
@RequestMappingan der Klasse und@GetMappingan der Methode? - Warum eignet sich ein Record gut für ein Antwortobjekt, aber schlecht für eine Klasse, deren Werte sich ändern sollen?
- Welchen Statuscode liefert ein Aufruf mit richtiger Adresse, aber falscher HTTP-Methode?
Antworten
- Weil die Anwendung ihren eigenen Webserver mitbringt und dafür eine besondere Projektstruktur und passende Abhängigkeiten braucht. Der Initializr stellt beides bereit.
- Die Annotation kennzeichnet die Klasse als REST-Controller. Spring findet sie beim Start automatisch und wandelt die Rückgabewerte ihrer Methoden in JSON um.
@RequestMappingan der Klasse legt den gemeinsamen Pfadanfang für alle Endpunkte fest.@GetMappingan der Methode bestimmt, dass genau diese MethodeGET-Anfragen beantwortet — und ergänzt gegebenenfalls den Pfad.- Ein Record ist unveränderlich. Ein Antwortobjekt wird einmal erzeugt und verschickt, es soll sich nicht mehr ändern. Eine Klasse, deren Werte sich ändern sollen, braucht dagegen Setter — die ein Record nicht hat.
405 Method Not Allowed.