16. Einführung in Spring MVC
16.1. Die Rolle von Spring MVC in einer Webanwendung
Betrachten wir Spring MVC im Kontext der Entwicklung einer Webanwendung. Meistens basiert diese auf einer mehrschichtigen Architektur wie der folgenden:
![]() |
- Die [Web]-Schicht ist die Schicht, die mit dem Benutzer der Webanwendung interagiert. Der Benutzer interagiert mit der Webanwendung über Webseiten, die in einem Browser angezeigt werden. Spring MVC befindet sich in dieser Schicht und nur in dieser Schicht;
- Die [Business]-Schicht implementiert die Geschäftslogik der Anwendung, wie beispielsweise die Berechnung eines Gehalts oder einer Rechnung. Diese Schicht nutzt Daten vom Benutzer über die [Web]-Schicht und aus dem DBMS über die [DAO]-Schicht;
- Die [DAO]-Schicht (Data Access Objects), die [ORM]-Schicht (Object Relational Mapper) und der JDBC-Treiber verwalten den Zugriff auf Daten im DBMS. Die [ORM]-Schicht fungiert als Brücke zwischen den von der [DAO]-Schicht verwalteten Objekten und den Zeilen und Spalten von Tabellen in einer relationalen Datenbank. Eine Spezifikation namens JPA (Java Persistence API) ermöglicht es Ihnen, das verwendete ORM zu abstrahieren, sofern es diese Spezifikationen implementiert. Dies ist in diesem Tutorial der Fall, daher werden wir die ORM-Schicht von nun an als JPA-Schicht bezeichnen;
- Die Integration dieser Schichten wird vom Spring-Framework übernommen;
16.2. Das Spring-MVC-Entwicklungsmodell
Spring MVC implementiert das MVC-Architekturmuster (Model–View–Controller) wie folgt:
![]() |
Die Bearbeitung einer Kundenanfrage läuft wie folgt ab:
- Anfrage – die angeforderten URLs haben die Form http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... Der [Front Controller] verwendet eine Konfigurationsdatei oder Java-Annotationen, um die Anfrage an den richtigen Controller und die richtige Aktion innerhalb dieses Controllers weiterzuleiten. Dazu nutzt er das Feld [Action] der URL. Der Rest der URL [/param1/param2/...] besteht aus optionalen Parametern, die an die Aktion übergeben werden. Das „C“ in MVC bezieht sich hier auf die Kette [Front Controller, Controller, Action]. Wenn kein Controller die angeforderte Aktion verarbeiten kann, antwortet der Webserver, dass die angeforderte URL nicht gefunden wurde.
- Verarbeitung
- (Fortsetzung)
- Die ausgewählte Aktion kann die Parameter verwenden, die ihr vom [Front Controller] übergeben wurden. Diese können aus verschiedenen Quellen stammen:
- dem Pfad [/param1/param2/...] der URL,
- die [p1=v1&p2=v2]-Parameter der URL,
- aus Parametern, die der Browser mit seiner Anfrage übermittelt hat;
- Bei der Verarbeitung der Benutzeranfrage benötigt die Aktion möglicherweise die [Business]-Schicht [2b]. Sobald die Anfrage des Clients verarbeitet wurde, kann sie verschiedene Antworten auslösen. Ein klassisches Beispiel ist:
- eine Fehlerseite, wenn die Anfrage nicht korrekt verarbeitet werden konnte
- ansonsten eine Bestätigungsseite
- Die Aktion weist eine bestimmte Ansicht an, diese darzustellen [3]. Diese Ansicht zeigt Daten an, die als View-Modell bezeichnet werden. Dies ist das „M“ in MVC. Die Aktion erstellt dieses View-Modell [2c] und weist eine Ansicht an, diese darzustellen [3];
- Antwort – Die ausgewählte Ansicht V verwendet das von der Aktion erstellte Modell M, um die dynamischen Teile der HTML-Antwort zu initialisieren, die sie an den Client senden muss, und sendet dann diese Antwort.
Bei einem Webdienst / JSON wird die vorstehende Architektur leicht modifiziert:
![]() |
- in [4a] wird das Modell, bei dem es sich um eine Java-Klasse handelt, durch eine JSON-Bibliothek in eine JSON-Zeichenkette umgewandelt;
- in [4b] wird diese JSON-Zeichenkette an den Browser gesendet;
Lassen Sie uns nun die Beziehung zwischen der MVC-Webarchitektur und der Schichtenarchitektur klären. Je nachdem, wie das Modell definiert ist, können diese beiden Konzepte miteinander in Verbindung stehen oder auch nicht. Betrachten wir eine einschichtige Spring-MVC-Webanwendung:
![]() |
Wenn wir die [Web]-Schicht mit Spring MVC implementieren, erhalten wir zwar eine MVC-Webarchitektur, jedoch keine mehrschichtige Architektur. Hier übernimmt die [Web]-Schicht alles: Darstellung, Geschäftslogik und Datenzugriff. Diese Aufgaben werden von den Aktionen ausgeführt.
Betrachten wir nun eine mehrschichtige Webarchitektur:
![]() |
Die [Web]-Schicht kann ohne Framework und ohne Befolgung des MVC-Modells implementiert werden. Wir haben dann eine mehrschichtige Architektur, aber die Web-Schicht implementiert das MVC-Modell nicht.
In der .NET-Welt lässt sich die oben genannte [Web]-Schicht beispielsweise mit ASP.NET MVC implementieren, was zu einer mehrschichtigen Architektur mit einer [Web]-Schicht im MVC-Stil führt. Anschließend können wir diese ASP.NET MVC-Schicht durch eine klassische ASP.NET-Schicht (WebForms) ersetzen, während der Rest (Geschäftslogik, DAO, ORM) unverändert bleibt. Wir haben dann eine Schichtenarchitektur mit einer [Web]-Schicht, die nicht mehr MVC-basiert ist.
In MVC haben wir gesagt, dass das M-Modell das der V-Ansicht sei, d. h. die Menge der von der V-Ansicht angezeigten Daten. Es gibt eine weitere Definition des M-Modells in MVC:
![]() |
Viele Autoren sind der Ansicht, dass das, was rechts von der [Web]-Schicht liegt, das M-Modell von MVC bildet. Um Mehrdeutigkeiten zu vermeiden, können wir uns auf Folgendes beziehen:
- das Domänenmodell, wenn wir uns auf alles rechts von der [Web]-Schicht beziehen
- das View-Modell, wenn wir uns auf die von einer Ansicht V angezeigten Daten beziehen
Im Folgenden bezieht sich der Begriff „M-Modell“ ausschließlich auf das Modell einer V-Ansicht.
16.3. Ein Web/JSON-Projekt mit Spring MVC
Die Website [http://spring.io/guides] bietet Einführungsanleitungen, um das Spring-Ökosystem kennenzulernen. Wir werden einer davon folgen, um die für ein Spring-MVC-Projekt erforderliche Maven-Konfiguration zu ermitteln.
16.3.1. Das Demo-Projekt
![]() |
- In [1] importieren wir einen der Spring-Leitfäden;
![]() |
- in [2] wählen wir das Beispiel [Rest Service] aus;
- in [3] wählen wir das Maven-Projekt aus;
- in [4] wählen wir die endgültige Version des Leitfadens aus;
- in [5] bestätigen wir;
- in [6] das importierte Projekt;
Webdienste, auf die über Standard-URLs zugegriffen werden kann und die JSON-Daten zurückgeben, werden oft als REST-Dienste (REpresentational State Transfer) bezeichnet. Ein Dienst gilt als RESTful, wenn er bestimmte Regeln befolgt.
Betrachten wir nun das importierte Projekt, beginnend mit seiner Maven-Konfiguration.
16.3.2. Maven-Konfiguration
Die Datei [pom.xml] sieht wie folgt aus:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<start-class>hello.Application</start-class>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
- Zeilen 6–8: Die Maven-Projekteigenschaften. Ein [<packaging>]-Tag, das den vom Maven-Build erzeugten Dateityp angibt, fehlt. In diesem Fall wird der Typ [jar] verwendet. Die Anwendung ist daher eine konsolenbasierte ausführbare Anwendung und keine Webanwendung; in diesem Fall wäre die Verpackung [war];
- Zeilen 10–14: Das Maven-Projekt hat ein übergeordnetes Projekt [spring-boot-starter-parent]. Dieses definiert die meisten Abhängigkeiten des Projekts. Diese können ausreichend sein – in diesem Fall werden keine zusätzlichen Abhängigkeiten hinzugefügt – oder auch nicht; in diesem Fall werden die fehlenden Abhängigkeiten hinzugefügt;
- Zeilen 17–20: Das Artefakt [spring-boot-starter-web] enthält die Bibliotheken, die für ein Spring-MVC-Webservice-Projekt erforderlich sind, bei dem keine Ansichten generiert werden. Dieses Artefakt umfasst eine sehr große Anzahl von Bibliotheken, darunter auch solche für einen eingebetteten Tomcat-Server. Die Anwendung wird auf diesem Server ausgeführt;
Die in dieser Konfiguration enthaltenen Bibliotheken sind zahlreich:
![]() | ![]() |
Oben sehen wir die drei Tomcat-Server-Archive.
16.3.3. Die Architektur eines Spring [Web/JSON]-Dienstes
Für einen Web-/JSON-Dienst implementiert Spring MVC das MVC-Modell wie folgt:
![]() |
- In [4a] wird das Modell – eine Java-Klasse – durch eine JSON-Bibliothek in eine JSON-Zeichenkette umgewandelt;
- in [4b] wird diese JSON-Zeichenkette an den Browser gesendet;
16.3.4. Der C-Controller
![]() |
Die importierte Anwendung verfügt über den folgenden Controller:
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
- Zeile 9: Die Annotation [@RestController] macht die Klasse [GreetingController] zu einem Spring-Controller, was bedeutet, dass ihre Methoden für die Verarbeitung von URLs registriert sind. Wir haben bereits die ähnliche Annotation [@Controller] gesehen. Der Rückgabetyp der Methoden dieses Controllers war [String], also der Name der anzuzeigenden Ansicht. Hier ist das anders. Die Methoden eines [@RestController] geben Objekte zurück, die serialisiert und an den Browser gesendet werden. Die Art der durchgeführten Serialisierung hängt von der Spring-MVC-Konfiguration ab. Hier werden sie in JSON serialisiert. Das Vorhandensein einer JSON-Bibliothek in den Projektabhängigkeiten bewirkt, dass Spring Boot das Projekt automatisch auf diese Weise konfiguriert;
- Zeile 14: Die Annotation [@RequestMapping] gibt die von der Methode bearbeitete URL an, in diesem Fall die URL [/greeting];
- Zeile 15: Die Annotation [@RequestParam] haben wir bereits erläutert. Das von der Methode zurückgegebene Ergebnis ist ein Objekt vom Typ [Greeting].
- Zeile 12: eine Long-Integer-Variable vom Typ „atomic“. Das bedeutet, dass sie parallelen Zugriff unterstützt. Es kann vorkommen, dass mehrere Threads gleichzeitig die Variable [counter] inkrementieren wollen. Dies wird ordnungsgemäß gehandhabt. Ein Thread kann den Wert des Zählers erst lesen, wenn der Thread, der ihn gerade ändert, seine Änderung abgeschlossen hat.
16.3.5. Das M-Modell
Das durch die vorherige Methode erzeugte M-Modell ist das folgende [Greeting]-Objekt:
![]() |
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
Die JSON-Umwandlung dieses Objekts erzeugt die Zeichenfolge {"id":n,"content":"text"}. Letztendlich hat die von der Controller-Methode erzeugte JSON-Zeichenfolge folgende Form:
oder
16.3.6. Ausführung
![]() |
Die Klasse [Application.java] ist die ausführbare Klasse des Projekts. Ihr Code lautet wie folgt:
package hello;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Wir sind diesem Code bereits im vorherigen Beispiel begegnet und haben ihn dort erklärt. Führen wir das Projekt aus:
![]() |
Wir erhalten die folgenden Konsolenprotokolle:
- Zeile 13: Der Tomcat-Server startet auf Port 8080 (Zeile 12);
- Zeile 17: Das Servlet [DispatcherServlet] ist vorhanden;
- Zeile 20: Die Methode [GreetingController.greeting] wurde gefunden;
Um die Webanwendung zu testen, rufen wir die URL [http://localhost:8080/greeting] auf:
![]() | ![]() |
Wir erhalten die erwartete JSON-Zeichenkette. Es könnte interessant sein, die vom Server gesendeten HTTP-Header anzusehen. Dazu verwenden wir die Chrome-Erweiterung namens [Advanced Rest Client] (Chrome / Strg-T / Menü [Anwendungen] / [Advanced Rest Client] – siehe Anhang, Abschnitt 23.11):
![]() |
- in [1] die angeforderte URL;
- in [2] wird die GET-Methode verwendet;
- in [3] die JSON-Antwort;
- in [4] hat der Server angegeben, dass er eine Antwort im JSON-Format sendet;
- in [5] wird dieselbe URL angefordert, diesmal jedoch mit einer POST-Anfrage;
- in [7] werden die Informationen im [urlencoded]-Format an den Server gesendet;
- in [6] der Parameter „name“ mit seinem Wert;
- in [8] teilt der Browser dem Server mit, dass er [urlencoded]-Daten sendet;
- in [9] die JSON-Antwort des Servers;
16.3.7. Erstellen eines ausführbaren Archivs
Wir erstellen nun ein ausführbares Archiv:
![]() |
![]() |
- in [1]: Wir führen ein Maven-Target aus;
- in [2]: Es gibt zwei Ziele: [clean], um den Ordner [target] aus dem Maven-Projekt zu löschen, und [package], um ihn neu zu generieren;
- in [3]: Der generierte Ordner [target] befindet sich in diesem Ordner;
- in [4]: Das Ziel wird generiert;
In den Protokollen, die in der Konsole angezeigt werden, ist es wichtig, dass [spring-boot-maven-plugin] aufgeführt ist. Dies ist das Plugin, das das ausführbare Archiv generiert (siehe [pom.xml] unten):
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Wechseln Sie in einer Eingabeaufforderung in den generierten Ordner:
D:\Temp\wksSTS\gs-rest-service\target>dir
...
11/06/2014 15:30 <DIR> classes
11/06/2014 15:30 <DIR> generated-sources
11/06/2014 15:30 11 073 572 gs-rest-service-0.1.0.jar
11/06/2014 15:30 3 690 gs-rest-service-0.1.0.jar.original
11/06/2014 15:30 <DIR> maven-archiver
11/06/2014 15:30 <DIR> maven-status
...
- Zeile 5: das generierte Archiv;
Dieses Archiv wird wie folgt ausgeführt:
D:\Temp\wksSTS\gs-rest-service-complete\target>java -jar gs-rest-service-0.1.0.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.1.0.RELEASE)
2014-06-11 15:32:47.088 INFO 4972 --- [ main] hello.Application
: Starting Application on Gportpers3 with PID 4972 (D:\Temp\wk
sSTS\gs-rest-service-complete\target\gs-rest-service-0.1.0.jar started by ST in
D:\Temp\wksSTS\gs-rest-service-complete\target)
...
Nachdem die Webanwendung nun ausgeführt wird, können Sie über einen Browser darauf zugreifen:
![]() |
16.3.8. Bereitstellung der Anwendung auf einem Tomcat-Server
Wie bereits beim vorherigen Projekt ändern wir die Datei [pom.xml] wie folgt:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<packaging>war</packaging>
...
</project>
- Zeile 9: Sie müssen angeben, dass Sie eine WAR-Datei (Web Archive) erstellen;
Außerdem müssen Sie die Webanwendung konfigurieren. Wenn keine [web.xml]-Datei vorhanden ist, erfolgt dies über eine Klasse, die [SpringBootServletInitializer] erweitert:
![]() |
Die Klasse [ApplicationInitializer] sieht wie folgt aus:
package hello;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class ApplicationInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
- Zeile 6: Die Klasse [ApplicationInitializer] erweitert die Klasse [SpringBootServletInitializer];
- Zeile 9: Die Methode [configure] wird überschrieben (Zeile 8);
- Zeile 10: Die Klasse, die das Projekt konfiguriert, wird bereitgestellt;
Um das Projekt auszuführen, gehen Sie wie folgt vor:
![]() |
- Führen Sie in [1-2] das Projekt auf einem der in der Eclipse-IDE registrierten Server aus;
Sobald dies geschehen ist, können Sie die URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] in einem Browser aufrufen:
![]() |
16.4. Fazit
Wir haben eine Art von Spring-MVC-Projekt vorgestellt, bei dem die Webanwendung einen JSON-Stream an den Browser sendet. Wir werden nun eine Web-/JSON-Anwendung entwickeln, um die in den vorangegangenen Kapiteln behandelte Datenbank [dbproduitscategories] im Web verfügbar zu machen.























