Verwendung von Spring Boot zur Einrichtung eines REST-Anwendungsservers

Illustration einer Rakete, die kurz vor dem Start steht, mit dem Logo von WATA Factory, die den gut geplanten Beginn eines Softwareprojekts vor dem Entwicklungsstart darstellt.

Die Einrichtung eines Backend-Servers zu Beginn eines neuen Projekts kann mitunter kompliziert und zeitaufwendig sein. In diesem Artikel zeigen wir, wie man mit Java und Spring Boot schnell und unkompliziert einen REST-Server aufsetzt.

Einleitung

Bei WATA Factory setzen wir Java sowohl in Kundenprojekten als auch intern intensiv ein. Trotz seines beachtlichen Alters von inzwischen 25 Jahren ist Java nach wie vor weit verbreitet und äußerst relevant.

Für das Frontend gibt es inzwischen meist bessere Alternativen wie Angular (mit TypeScript), React (mit JavaScript) oder Flutter (mit Dart). Doch im Backend ist Java oft nach wie vor die beste Wahl und kommt in unterschiedlichsten Projekten zum Einsatz. Gerade bei großen, unternehmensweiten Anwendungen gilt Java weiterhin quasi als Industriestandard.

Natürlich stehen wir alle auf den Schultern von Giganten – niemand käme auf die Idee, eine komplette Anwendung völlig von Grund auf neu zu entwickeln. Stattdessen greifen wir auf Anwendungs-Frameworks zurück. Im Java-Ökosystem gibt es zahlreiche Optionen, deren Eignung sich jeweils nach den Projektanforderungen richtet: 

  • Jakarta EE (ehemals Java EE) – für große Enterprise-Anwendungen
  • Hibernate – ein Java-ORM-Framework, das mit JPA arbeitet und den Datenbankzugriff vereinfacht
  • Quarkus – leichtgewichtig, ideal für Cloud und Microservices
  • Spring Framework – vermutlich das populärste Framework, geeignet für Microservices und REST-Anwendungen

In diesem Artikel konzentrieren wir uns auf Spring, genauer gesagt auf Spring Boot. Es baut auf dem Spring Framework auf und wurde entwickelt, um die Entwicklung eigenständiger, produktionsreifer Anwendungen deutlich zu vereinfachen.

Warum Spring Boot?

Traditionell erforderte die Entwicklung neuer Anwendungen mit Java EE oder dem reinen Spring Framework viel Konfiguration und Boilerplate-Code. Man musste Abhängigkeiten verwalten, sicherstellen, dass alle Komponenten miteinander funktionieren, und sich zusätzlich um das Deployment auf einem geeigneten Anwendungsserver kümmern – oftmals ein aufwändiger Prozess.

Für umfangreiche Enterprise-Anwendungen mag dieser Detaillierungsgrad notwendig sein, doch für kleinere Projekte bedeutet er schlicht unnötigen Overhead. Genau hier kommt Spring Boot ins Spiel: Es wurde entwickelt, um die Entwicklung eigenständiger, produktionsfertiger Spring-Anwendungen zu vereinfachen, indem es weitestgehend auf Konventionen statt auf explizite Konfiguration setzt.

Convention over Configuration

Das Prinzip „Convention over Configuration“ bedeutet, dass Spring Boot sinnvolle Standardwerte vorgibt, die in vielen Fällen keine weitere manuelle Anpassung erfordern. So wird beispielsweise automatisch ein eingebetteter Tomcat-Server mitgeliefert, der sofort einsatzbereit ist.

Auch viele Komponenten werden automatisch anhand von Standardwerten und vorhandenen Abhängigkeiten konfiguriert. Dadurch reduziert sich der Umfang an Boilerplate-Code erheblich, und man kann sehr schnell produktiv loslegen.

Der Spring Boot Initializr

Ein weiteres Werkzeug, das den Projektstart enorm erleichtert, ist der Spring Boot Initializr. Mit diesem webbasierten Tool lässt sich ein vollständiges Projekt vorkonfigurieren und generieren. Dadurch entfällt der manuelle Aufbau der Projektstruktur.

Erstellen einer Demo-Anwendung

Schauen wir uns nun an, wie wir mit dem Initializr eine funktionierende Spring-Boot-Anwendung aufsetzen:

Aufrufen von https://start.spring.io/

Screenshot der Spring Boot-Oberfläche zum Erstellen eines neuen Projekts, mit Konfigurationsoptionen wie Projektdaten, Abhängigkeiten und Build-Tools
  • Als Build-Tool wählen wir Maven (Gradle wäre ebenfalls möglich, doch Maven gilt als bewährt und bietet unzählige Plugins).
  • Als Programmiersprache wählen wir Java.
  • Die Spring Boot-Version belassen wir auf der jeweils aktuellen stabilen Version.
  • Metadaten für dieses Demo-Projekt können wir auf den Standardwerten belassen.
  • Als Packaging wählen wir „Jar“, damit unsere Anwendung als ausführbare JAR-Datei vorliegt.
  • Wir wählen die Java-Version, die lokal auf unserem System installiert ist.

Nun folgt der interessante Teil: Unter „Add Dependencies“ können wir komfortabel sämtliche benötigten Abhängigkeiten hinzufügen, ohne manuell in Maven Central suchen zu müssen. Zur Verfügung stehen unter anderem Tools für Entwicklung, Web, Datenbanken (SQL und NoSQL), Cloud, Sicherheit, Templating, Messaging, Tests, KI und vieles mehr.

Screenshot der Abhängigkeitsauswahlseite in Spring Boot, auf der Bibliotheken wie Spring Web, Spring Data JPA und H2 Database ausgewählt werden

Für unsere kleine Demo wählen wir folgende Abhängigkeiten aus: 

  • Spring Web: Ermöglicht das Erstellen von REST-Endpunkten und bringt den eingebetteten Tomcat-Server mit.
  • Spring Boot DevTools: Erleichtert die Entwicklung durch Funktionen wie Live Reload und schnelleren Start.
  • Lombok: Spart Boilerplate-Code bei POJOs.
  • Spring Data JPA: Für den Datenbankzugriff.
  • H2 Database: Eine einfache In-Memory-Datenbank für dieses Projekt.. 

Nach dem Klick auf „Generate“ wird ein ZIP-Archiv heruntergeladen. Dieses Archiv entpacken wir und öffnen das Projekt anschließend mit der IDE unserer Wahl. In diesem Beispiel verwenden wir IntelliJ.

Der Initializr hat nun eine Projektstruktur erzeugt (es gibt noch weitere Ordner und Dateien, aber diese sind die wichtigsten):

  • demo/src/main/java/com/example/demo/DemoApplication.java
    Hauptklasse zum Starten der Anwendung. 
  • Demo/src/main/resources/application.properties
    Hauptkonfigurationsdatei für die Anwendung. Hier werden z. B. die Datenbankverbindung und weitere Einstellungen hinterlegt.
  • pom.xml
    Verwaltung der Abhängigkeiten und Build-Plugins.

Erstellen des ersten Endpoints

Nachdem die Basis steht, erstellen wir unseren ersten, sehr einfachen REST-Endpunkt:

Wir erzeugen im gleichen Paket wie DemoApplication.java eine neue Datei HelloWataController.java mit folgendem Inhalt:

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloWataController {

    @GetMapping
    public String sayHello() {
        return "Hello WATA Factory!";
    }
}

Und das ist Alles!

Jetzt starten wir die Anwendung, indem wir DemoApplication.java ausführen. Bevor wir dies tun, sollten wir sicherstellen, dass alle Abhängigkeiten korrekt installiert sind, indem wir Maven clean und install ausführen.

Anschließend öffnen wir unseren Browser und rufen http://localhost:8080/hello auf. Wie erwartet, erscheint die Ausgabe „Hello WATA Factory!“.

Damit haben wir erfolgreich unseren ersten REST-Endpunkt mit Spring Boot erstellt, der bereits mit minimalem Aufwand funktionsfähig ist. 

Erweiterung um eine Datenbank

Im nächsten Schritt erweitern wir unsere Anwendung um eine Datenbankanbindung. Dank des H2-Datenbank-Pakets, das wir bereits hinzugefügt haben, ist dies unkompliziert. Für dieses Beispiel erstellen wir eine kleine Buch-Datenbank.

Bevor wir jedoch mit der Programmierung fortfahren, müssen wir die Verbindung zur Datenbank konfigurieren. Diese Einstellungen nehmen wir in der Datei application.properties vor: 

spring.application.name=demo

spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update

Definition der Entity-Klasse

Als nächstes erstellen wir unsere Entitäts-POJO-Klasse. Dazu erstellen wir ein Paket namens „book“ unterhalb von „com.example.demo“ und erstellen in diesem Paket eine neue Datei namens Book.java. Fügen Sie den folgenden Code in die neu erstellte Klasse ein:

@Entity
@Getter
@Setter
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String author;
}

Dies ist unsere Entity-Klasse, die die Datenbankeinträge der Tabelle „book“ abbildet, was durch die Annotation „@Entity“ gekennzeichnet wird.

Die Annotationen „@Getter“ und „@Setter“ stammen aus dem im Initializr hinzugefügten Lombok-Paket. Sie generieren automatisch Getter- und Setter-Methoden für die Attribute zur Compile-Zeit, sodass wir diese Methoden nicht manuell schreiben müssen.

Die weiteren beiden Annotationen legen fest, welches Attribut der Primärschlüssel der Entity ist und wie dieser generiert wird.

In Spring Boot bezeichnet man die Klasse, die Entities aus der Datenbank liest und in die Datenbank schreibt, als Repository. Daher müssen wir nun eine Repository-Klasse für unsere Book-Entity erstellen. Wir nennen sie BookRepository.java und legen sie im gleichen Paket wie die Entity-Klasse ab. Der Code besteht nur aus einer Zeile:

public interface BookRepository extends JpaRepository<Book, Long> { }

Service-Layer erstellen

In einer klassischen Drei-Schichten-Architektur greift man üblicherweise nicht direkt vom Controller auf das Repository zu. Dies dient dazu, die verschiedenen Ebenen der Anwendung voneinander zu trennen, was eine bewährte Praxis darstellt. Stattdessen erfolgt der Zugriff auf die Datenbank über eine Service-Klasse.

Lassen Sie uns diese Klasse erstellen und, wenig überraschend, „BookService.java“ nennen: 

@Service
public class BookService {
 
    @Autowired
    private BookRepository bookRepository;
 
    public List<Book> getAllBooks() {
        return bookRepository.findAll();
    }
 
    public Book getBookById(Long id) {
        return bookRepository.findById(id).orElse(null);
    }
 
    public void deleteBook(Long id) {
        bookRepository.deleteById(id);
    }
}

Zusammengefasst haben wir also drei Methoden: eine zum Abrufen aller Bücher, eine zum Suchen eines Buches anhand seiner ID und eine zum Löschen eines Buches aus der Datenbank.

Aber Moment – wir haben diese Methoden in unserem BookRepository.java gar nicht selbst implementiert! Tatsächlich besteht unsere Repository-Klasse nur aus einer einzigen Zeile.

Das ist eine weitere Stärke von Spring Boot und ein schönes Beispiel für das Prinzip „Convention over Configuration“: Diese Methoden funktionieren einfach direkt – ganz ohne eigene Implementierung. Spring Boot bietet zahlreiche Möglichkeiten, um auf Datenbanken zuzugreifen, ohne auch nur eine einzige SQL-Zeile schreiben zu müssen. 

Definition des Controller 

Nun fehlt nur noch ein letzter Schritt, um unsere Anwendung vollständig nutzbar zu machen: Wir müssen die zuvor erstellten Methoden nach außen verfügbar machen. Dazu erstellen wir eine Controller-Klasse, die drei REST-Endpunkte bereitstellt, je einen pro Methode:

@RestController
@RequestMapping("/api/books")
public class BookController {
 
    @Autowired
    private BookService bookService;
 
    @GetMapping
    public List<Book> getAllBooks() {
        return bookService.getAllBooks();
    }
 
    @GetMapping("/{id}")
    public Book getBookById(@PathVariable Long id) {
        return bookService.getBookById(id);
    }
 
    @DeleteMapping("/{id}")
    public void deleteBook(@PathVariable Long id) {
        bookService.deleteBook(id);
    }
}

Befüllen der Datenbank

Wir starten die Anwendung erneut. Da wir eine In-Memory-Datenbank verwenden, werden alle Daten bei jedem Herunterfahren der Anwendung gelöscht. Daher müssen wir zunächst wieder einige Testdaten einfügen, die wir über unsere Endpunkte abfragen können.

Dazu öffnen wir im Browser die Adresse http://localhost:8080/h2-console. Mit den in der Datei application.properties definierten Zugangsdaten stellen wir die Verbindung zur In-Memory-Datenbank her. Anschließend können wir im angezeigten SQL-Editor einige Insert-Befehle ausführen, um die Datenbank mit Büchern zu füllen. Hier einige Beispiele:

INSERT INTO BOOK VALUES(1, 'George Orwell', '1984');
INSERT INTO BOOK VALUES(2, 'J.R.R. Tolkien', 'The Lord of the Rings');
INSERT INTO BOOK VALUES(3, 'Franz Kafka', 'Metamorphosis');

Falls Sie (wie ich) es als lästig empfinden, diesen Schritt bei jedem Neustart der Anwendung zu wiederholen, können Sie diese Einfügungen einfach in einer Datei namens data.sql speichern und diese im Verzeichnis src/main/resources ablegen. Außerdem müssen Sie in Falls Sie (wie ich) es als lästig empfinden, diesen Schritt bei jedem Neustart der Anwendung zu wiederholen, können Sie diese Einfügungen einfach in einer Datei namens data.sql speichern und diese im Verzeichnis src/main/resources ablegen. Außerdem müssen Sie in application.properties die Zeile  

spring.datasource.url=jdbc:h2:mem:testdb

In

spring.datasource.url=jdbc:h2:file:./data/demo

Sie werden nun bei jedem Start der Anwendung automatisch ausgeführt.

Dies ist ein weiteres gutes Beispiel für die Spring Boot Philosophie „Konvention vor Konfiguration“: Man könnte natürlich auch einen anderen Dateinamen und ein anderes Verzeichnis konfigurieren, aber wenn man sich mit diesen vernünftigen Standardwerten zufrieden gibt, funktioniert es sofort, ohne dass man überhaupt etwas konfigurieren muss.

Testen der Anwendung

Nachdem wir nun einige Daten in unsere Datenbank eingegeben haben, können wir unsere Endpunkte testen. Am einfachsten geht das mit einem Tool wie Postman, mit dem Sie die verschiedenen http-Methoden wie GET und DELETE einfach ausprobieren können. Wenn Sie die Kommandozeile bevorzugen, könnte curl das Tool Ihrer Wahl sein.

Wenn Sie eine GET-Anfrage an http://localhost:8080/api/books stellen, sollten Sie folgende Antwort erhalten: 

Postman-Oberfläche mit einer GET-Anfrage an einen Spring Boot REST-API-Endpunkt, um eine Liste aller Bücher vom Backend abzurufen

Um nur das “book” mit id=2 abzurufen, rufen Sie http://localhost:8080/api/books/2

Postman-Oberfläche zeigt das Ergebnis einer GET-Anfrage an einen Spring Boot REST-API-Endpunkt, der die Details eines einzelnen Buches nach ID zurückgibt

Löschen wir nun das “book” mit der id=2 aus der Datenbank, indem wir eine DELETE-Anfrage stellen und dann erneut alle Bücher abrufen, um sicherzustellen, dass es wirklich weg ist:

Postman-Anwendung mit einer DELETE-Anfrage an einen Spring Boot REST-API-Endpunkt, um einen bestimmten Bucheintrag aus der Datenbank zu löschen
Einsatz von Postman zur Testung einer Spring Boot REST-API durch Senden einer GET-Anfrage zum Abrufen aller Bücher, inklusive Anzeige der JSON-Antwort

Alles erledigt! Wie wir bereits getestet haben, funktionieren unsere Endpunkte wie erwartet.

Natürlich ist dieses kleine Beispiel nur ein Proof of Concept. Es fehlt an Sicherheit, Tests und natürlich an einem Frontend.  

Fazit

In der WATA Factory haben wir Java für unser Backend sowohl in internen als auch in externen Projekten eingesetzt und tun dies auch weiterhin. Obwohl es oft als umständlicher und schwieriger einzurichten wahrgenommen wird als z.B. Javascript-basierte Alternativen wie NodeJS, hat dieser Artikel gezeigt, wie mit wenigen Zeilen Code ein funktionierender Anwendungsserver eingerichtet werden kann, um so schnell wie möglich produktiv zu werden.