Skip to content

1. Einführung

Die PDF-Datei dieses Dokuments ist verfügbar |HIER|.

Die Beispiele dieses Dokuments sind unter |HIER| verfügbar.

Wir möchten hier anhand von Beispielen die wichtigen Konzepte von Spring MVC vorstellen, einem Java-Web-Framework, das einen Rahmen für die Entwicklung von Webanwendungen nach dem MVC-Modell (Model – View – Controller) bietet. Spring MVC ist ein Teil des Spring-Ökosystems [http://projects.spring.io/spring-framework/]. Außerdem stellen wir die View-Engine Thymeleaf [http://www.thymeleaf.org/] vor.

Dieser Kurs richtet sich an Leser, die die Programmiersprache Java wirklich beherrschen. Kenntnisse in der Webprogrammierung sind nicht erforderlich.

Obwohl dieses Dokument ausführlich ist, ist es wahrscheinlich unvollständig. Spring ist ein riesiges Framework mit zahlreichen Verzweigungen. Um sich näher mit Spring MVC zu befassen, können die folgenden Referenzen herangezogen werden:

  • das Referenzdokument zum Spring-Framework [http://docs.spring.io/spring/docs/current/spring-framework-reference/pdf/spring-framework-reference.pdf];
  • Zahlreiche Spring-Tutorials finden Sie auf der URL [http://spring.io/guides]
  • die Website von [developpez.com], die sich mit Spring [http://spring.developpez.com/] befasst.

Das Dokument wurde so verfasst, dass es auch ohne Computer gelesen werden kann. Daher enthält es zahlreiche Screenshots.

1.1. Sources

Dieses Dokument stützt sich auf zwei Hauptquellen:

  • [Einführung in das ASP.NET MVC-Framework anhand von Beispielen (2013)]. Spring MVC und ASP.NET MVC sind zwei ähnliche Frameworks, wobei das zweite erst lange nach dem ersten entwickelt wurde. Um die beiden Frameworks vergleichen zu können, habe ich denselben Aufbau wie im Dokument zu ASP.NET und MVC übernommen;
  • das Dokument zu ASP.NET MVC enthält derzeit (Dez. 2014) keine Fallstudie mit einer entsprechenden Lösung. Ich habe hier die aus dem Dokument [Tutoriel AngularJS / Spring 4] übernommen und wie folgt angepasst:
    • Die Fallstudie in [Ein Client/Server-Beispiel – AngularJS 1.x / Spring 4 (2014)] betrifft eine Client-Server-Anwendung, bei der der Server ein mit Spring erstellter Webdienst (jSON) und der Client ein Client (AngularJS) ist.
    • in diesem Dokument wird derselbe Webdienst / jSON verwendet, der Client ist jedoch eine zweischichtige Webanwendung [client jQuery] / [service web / jSON];

Abgesehen von diesen Quellen habe ich im Internet nach Antworten auf meine Fragen gesucht. Vor allem die Website [http://stackoverflow.com/] war mir dabei hilfreich.

1.2. Verwendete Tools

Die folgenden Beispiele wurden in der folgenden Umgebung getestet:

  • Windows 8.1 Pro 64-Bit-Rechner;
  • JDK 1.8;
  • IDE Spring Tool Suite 3.6.3 (siehe Abschnitt 9.3);
  • Chrome-Browser (andere Browser wurden nicht verwendet);
  • Chrome-Erweiterung [Advanced Rest Client] (siehe Abschnitt 9.6);

Achtung bei JDK 1.8. Eine der Methoden der Fallstudie nutzt eine Methode aus dem Java-8-Paket [java.lang].

Alle Beispiele sind Maven-Projekte, die wahlweise mit Eclipse, IDE, IntellijIDEA oder NetBeans geöffnet werden können. Im Folgenden stammen die Screenshots aus der IDE Spring Tool Suite, einer Variante von Eclipse.

1.3. Die Beispiele

Die Beispiele stehen unter den Bezeichnungen URL und [http://tahe.developpez.com/java/springmvc-thymeleaf] als herunterladbare Datei zip zur Verfügung.

  

Um alle Projekte in STS zu laden, gehen Sie wie folgt vor:

  • Importieren Sie in [1-3] Maven-Projekte;
  • in [4] den Ordner mit den Beispielen angeben;
  • in [5] wählen Sie alle Projekte im Ordner aus;
  • in [6], bestätigen Sie;
  • in [7] die importierten Projekte;

1.4. Die Rolle von Spring MVC in einer Webanwendung

Betrachten wir Spring MVC im Rahmen der Entwicklung einer Webanwendung. Meistens basiert diese auf einer mehrschichtigen Architektur wie der folgenden:

  • Die Schicht [Web] ist die Schicht, die mit dem Benutzer der Webanwendung in Kontakt steht. Dieser interagiert mit der Webanwendung über Webseiten, die in einem Browser angezeigt werden. In dieser Schicht befindet sich Spring MVC und ausschließlich in dieser Schicht;
  • Die Schicht [métier] implementiert die Geschäftsregeln der Anwendung, wie beispielsweise die Berechnung eines Gehalts oder einer Rechnung. Diese Schicht nutzt Daten, die vom Benutzer über die Schicht [Web] und von SGBD über die Schicht [DAO] bereitgestellt werden;
  • die Schicht [DAO] (Data Access Objects), die Schicht [ORM] (Object Relational Mapper) und der Treiber JDBC verwalten den Zugriff auf die Daten der Schicht SGBD. Die Schicht [ORM] bildet eine Brücke zwischen den von der Schicht [DAO] verarbeiteten Objekten und den Zeilen und Spalten der Tabellen einer relationalen Datenbank. Wir werden hier Hibernate (ORM) verwenden. Eine Spezifikation namens JPA (Java Persistence API) ermöglicht es, sich von der verwendeten ORM-Schicht zu abstrahieren, sofern diese die genannten Spezifikationen implementiert. Dies ist bei Hibernate und anderen Java-ORM der Fall. Wir werden die ORM-Schicht fortan als JPA-Schicht bezeichnen;
  • die Integration der Schichten erfolgt durch das Spring-Framework;

Die meisten der im Folgenden aufgeführten Beispiele verwenden nur eine einzige Schicht, nämlich die Schicht [Web]:

Dieses Dokument endet jedoch mit der Erstellung einer mehrschichtigen Webanwendung:

Der Browser stellt eine Verbindung zu einer Anwendung [Web1] her, die mit Spring MVC / Thymeleaf implementiert ist und ihre Daten von einem Webservice [Web2] abruft, der ebenfalls mit Spring MVC implementiert ist. Diese zweite Webanwendung greift auf eine Datenbank zu.

1.5. Das Entwicklungsmodell von Spring MVC

Spring MVC implementiert das sogenannte MVC-Architekturmodell (Model-View-Controller) wie folgt:

Die Bearbeitung einer Client-Anfrage läuft wie folgt ab:

  1. Anfrage – die angeforderten URL haben die Form http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&.... Das [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] des URL. Der Rest des URL [/param1/param2/...] besteht aus optionalen Parametern, die an die Aktion übergeben werden. Das C in MVC ist hier die Zeichenkette [Front Controller, Contrôleur, Action]. Wenn kein Controller die angeforderte Aktion verarbeiten kann, antwortet der Webserver, dass die angeforderte URL nicht gefunden wurde.
  1. Verarbeitung
  • Die ausgewählte Aktion kann die Parameter parami nutzen, die ihr von [Front Controller] übergeben wurden. Diese können aus verschiedenen Quellen stammen:
    • aus dem Pfad [/param1/param2/...] des URL,
    • aus den Parametern [p1=v1&p2=v2] von URL,
    • aus Parametern, die der Browser mit seiner Anfrage übermittelt hat;
  • Bei der Bearbeitung der Benutzeranfrage benötigt die Aktion möglicherweise die Schichten [métier] und [2b]. Sobald die Anfrage des Clients bearbeitet wurde, kann dies verschiedene Antworten auslösen. Ein klassisches Beispiel ist:
    • eine Fehlerseite, wenn die Anfrage nicht korrekt verarbeitet werden konnte
    • ansonsten eine Bestätigungsseite
  • die Aktion fordert die Anzeige einer bestimmten Ansicht an: [3]. Diese Ansicht zeigt Daten an, die als Modell der Ansicht bezeichnet werden. Das ist das M in MVC. Die Aktion erstellt dieses Modell M [2c] und fordert eine Ansicht V auf, sich anzuzeigen: [3];
  1. Antwort – die ausgewählte Ansicht V verwendet das von der Aktion erstellte Modell M, um die dynamischen Teile der Antwort HTML zu initialisieren, die sie an den Client senden muss, und sendet diese Antwort anschließend.

Lassen Sie uns nun den Zusammenhang zwischen der Webarchitektur MVC und der Schichtenarchitektur näher erläutern. Je nachdem, wie man das Modell definiert, stehen diese beiden Konzepte in einem Zusammenhang oder auch nicht. Nehmen wir eine einschichtige Spring-Webanwendung MVC:

Wenn wir die Schicht [Web] mit Spring MVC implementieren, erhalten wir zwar eine Webarchitektur MVC, aber keine mehrschichtige Architektur. Hier übernimmt die Schicht [web] alles: Darstellung, Geschäftslogik, Datenzugriff. Diese Aufgaben werden von den Aktionen übernommen.

Betrachten wir nun eine mehrschichtige Webarchitektur:

Die Schicht [Web] kann ohne Framework und ohne Befolgung des Modells MVC implementiert werden. Man hat dann zwar eine mehrschichtige Architektur, aber die Webschicht implementiert das Modell MVC nicht.

Beispielsweise kann in der Welt .NET die oben genannte Schicht [Web]oben mit ASP.NET und MVC implementiert werden, und man erhält somit eine Schichtenarchitektur mit einer Schicht [Web] vom Typ MVC. Ist dies geschehen, kann man diese Schicht ASP.NET MVC durch eine klassische Schicht ASP.NET (WebForms) ersetzen, während der Rest (Geschäftslogik, DAO, ORM) unverändert beibehalten. Man erhält somit eine Schichtenarchitektur mit einer Schicht [Web], die nicht mehr vom Typ MVC ist.

In MVC haben wir festgelegt, dass das Modell M das der Ansicht V, c.a.d, ist. Die Gesamtheit der von der Ansicht V angezeigten Daten. Eine weitere Definition des Modells M von MVC lautet:

Viele Autoren sind der Ansicht, dass das, was sich rechts von der Ebene [Web] befindet, das Modell M von MVC bildet. Um Mehrdeutigkeiten zu vermeiden, kann man sprechen von:

  • vom Domänenmodell, wenn man alles bezeichnet, was rechts von der Ebene [Web] liegt
  • vom Modell der Ansicht, wenn man die von einer Ansicht V angezeigten Daten bezeichnet

Im Folgenden bezeichnet der Begriff „M-Modell“ ausschließlich das Modell einer Ansicht V.

1.6. Ein erstes Spring-Projekt MVC

Von nun an arbeiten wir mit der Spring Tool Suite (IDE), einer für Spring angepassten Eclipse-Variante. Die Website [http://spring.io/guides] bietet Einführungs-Tutorials, um das Spring-Ökosystem kennenzulernen. Wir werden einem davon folgen, um die für ein Spring-Projekt erforderliche Maven-Konfiguration kennenzulernen.

Hinweis: Die meisten Anfänger werden die Details des Projekts nicht verstehen. Das ist nicht wichtig. Diese Details werden im weiteren Verlauf des Dokuments erläutert. Wir beschränken uns darauf, die Schritte nachzuvollziehen.

1.6.1. Das Demonstrationsprojekt

  • in [1] importieren wir eine der Spring-Anleitungen;
  • in [2] wählen wir das Beispiel [Serving Web Content] aus;
  • in [3] wählen wir das Maven-Projekt aus;
  • in [4] wählen wir die endgültige Version der Anleitung aus;
  • in [5] bestätigen wir;
  • in [6] das importierte Projekt;

Sehen wir uns das Projekt an, zunächst seine Maven-Konfiguration.

1.6.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-serving-web-content</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</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-milestone</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestone</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>

</project>
  • Zeilen 6–8: die Eigenschaften des Maven-Projekts. Es fehlt ein Tag [<packaging>], das den Typ der durch die Maven-Kompilierung erzeugten Datei angibt. Da dieses fehlt, wird der Typ [jar] verwendet. Die Anwendung ist also eine ausführbare Konsolenanwendung und keine Webanwendung, bei der das Packaging dann [war] lauten würde;
  • Zeilen 10–14: Das Maven-Projekt hat ein übergeordnetes Projekt mit dem Typ [spring-boot-starter-parent]. Dieses definiert den Großteil der Projektabhängigkeiten. Sind diese ausreichend, werden keine weiteren hinzugefügt; sind sie nicht ausreichend, werden die fehlenden Abhängigkeiten hinzugefügt;
  • Zeilen 17–20: Das Artefakt [spring-boot-starter-thymeleaf] enthält die Bibliotheken, die für ein Spring-Projekt MVC erforderlich sind, das zusammen mit einer View-Engine namens [Thymeleaf] verwendet wird. Dieses Artefakt enthält eine sehr große Anzahl von Bibliotheken, darunter auch die eines eingebetteten Tomcat-Servers. Auf diesem Server wird die Anwendung ausgeführt;

Die von dieser Konfiguration bereitgestellten Bibliotheken sind sehr zahlreich:

Oben sind die Archive des Tomcat-Servers zu sehen.

Spring Boot ist ein Zweig des Spring-Ökosystems [http://projects.spring.io/spring-boot/]. Dieses Projekt zielt darauf ab, den Konfigurationsaufwand für Spring-Projekte so weit wie möglich zu reduzieren. Zu diesem Zweck führt Spring Boot eine automatische Konfiguration anhand der im Classpath des Projekts vorhandenen Abhängigkeiten durch. Spring Boot stellt zahlreiche gebrauchsfertige Abhängigkeiten bereit. So liefert die im vorherigen Maven-Projekt gefundene Abhängigkeit [spring-boot-starter-thymeleaf] alle notwendigen Abhängigkeiten für eine Spring-Anwendung MVC, die die View-Engine [Thymeleaf] verwendet. Mit diesen beiden Merkmalen:

  • gebrauchsfertige Abhängigkeiten;
  • automatische Konfiguration auf Basis dieser Abhängigkeiten und „vernünftiger“ Standardwerte lässt sich sehr schnell eine betriebsbereite Spring-Anwendung MVC erstellen. Dies ist bei dem hier untersuchten Projekt der Fall;

1.6.3. Die Architektur einer Spring-Anwendung MVC

Spring MVC implementiert das sogenannte MVC-Architekturmodell (Model – View – Controller):

Die Bearbeitung einer Client-Anfrage läuft wie folgt ab:

  1. Anfrage – die angeforderten URL haben die Form http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... Die [Dispatcher Servlet] ist die Spring-Klasse, die eingehende URL verarbeitet. Sie „leitet“ das URL an die Aktion weiter, die es verarbeiten soll. Diese Aktionen sind Methoden bestimmter Klassen, die als [Contrôleurs] bezeichnet werden. Das C in MVC ist hier die Zeichenkette [Dispatcher Servlet, Contrôleur, Action]. Wenn keine Aktion zur Verarbeitung des eingehenden URL konfiguriert wurde, antwortet das Servlet [Dispatcher Servlet], dass das angeforderte URL nicht gefunden wurde (Fehler 404 NOT FOUND);
  2. Verarbeitung
  • Die ausgewählte Aktion kann die Parameter parami nutzen, die ihr das Servlet [Dispatcher Servlet] übermittelt hat. Diese können aus verschiedenen Quellen stammen:
    • aus dem Pfad [/param1/param2/...] des URL,
    • aus den Parametern [p1=v1&p2=v2] des URL,
    • aus Parametern, die der Browser mit seiner Anfrage übermittelt hat;
  • Bei der Bearbeitung der Benutzeranfrage benötigt die Aktion möglicherweise die Schichten [metier] und [2b]. Sobald die Client-Anfrage bearbeitet wurde, kann sie verschiedene Antworten auslösen. Ein klassisches Beispiel ist:
    • eine Fehlerseite, falls die Anfrage nicht korrekt verarbeitet werden konnte
    • ansonsten eine Bestätigungsseite
  • Die Aktion fordert an, dass eine bestimmte Ansicht angezeigt wird: [3]. Diese Ansicht zeigt Daten an, die als Modell der Ansicht bezeichnet werden. Das ist das M in MVC. Die Aktion erstellt dieses Modell M [2c] und fordert eine Ansicht V auf, sich anzuzeigen [3];
  1. Antwort – die ausgewählte Ansicht V verwendet die von der Aktion erstellte Vorlage M, um die dynamischen Teile der Antwort HTML zu initialisieren, die sie an den Client senden muss, und sendet diese Antwort anschließend.

Wir werden uns diese verschiedenen Elemente im untersuchten Projekt genauer ansehen.

1.6.4. Der Controller C

  

Die importierte Anwendung verfügt über den folgenden Controller:


package hello;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class GreetingController {

    @RequestMapping("/greeting")
    public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
        model.addAttribute("name", name);
        return "greeting";
    }

}
  • Zeile 8: Die Annotation [@Controller] macht die Klasse [GreetingController] zu einem Spring-Controller, d. h., ihre Methoden werden registriert, um URL zu verarbeiten. Ein Spring-Controller ist ein Singleton. Es wird nur ein einziges Exemplar davon erstellt;
  • Zeile 11: Die Annotation [@RequestMapping] gibt das URL an, das von der Methode verarbeitet wird, in diesem Fall das URL [/greeting]. Wir werden später sehen, dass dieses URL parametrisiert werden kann und dass es möglich ist, diese Parameter abzurufen;
  • Zeile 12: Die Methode akzeptiert zwei Parameter:
    • [String name]: Dieser Parameter wird durch einen Parameter mit dem Namen [name] in der verarbeiteten Anfrage initialisiert, zum Beispiel [/greeting?name=alfonse]. Dieser Parameter ist optional ([required=false]), und wenn er nicht vorhanden ist, nimmt der Parameter [name] den Wert „World“ ([defaultValue="World"]) an,
    • [Model model] ist ein Ansichtsmodell. Es wird leer übergeben, und es ist die Aufgabe der Aktion (der Methode greeting), es zu füllen. Dieses Modell wird an die Ansicht übergeben, die von der Aktion angezeigt wird. Es handelt sich also um ein Ansichtsmodell;
  • Zeile 13: Der Wert von [name] wird in das View-Modell eingefügt. Die Klasse [Model] verhält sich wie ein Dictionary;
  • Zeile 14: Die Methode gibt den Namen der Ansicht zurück, die das erstellte Modell anzeigen soll. Der genaue Name der Ansicht hängt von der Konfiguration von [Thymeleaf] ab. Liegt keine solche Konfiguration vor, wird die hier angezeigte Ansicht die Ansicht [/templates/greeting.html] sein, oder der Ordner [templates] muss sich im Stammverzeichnis des Classpath des Projekts befinden;

Sehen wir uns unser Eclipse-Projekt an:

Die Ordner „[src/main/java]“ und „[src/main/resources]“ sind beides Ordner, deren Inhalt in den Classpath des Projekts aufgenommen wird. Bei „[src/main/java]“ werden die kompilierten Versionen der Java-Quellcodes dort abgelegt. Der Inhalt des Ordners [src/main/resources] wird hingegen unverändert in den Klassenpfad aufgenommen. Man sieht also, dass der Ordner [templates] im Klassenpfad des Projekts [1] enthalten sein wird.

Dies lässt sich im Eclipse-Fenster „[Navigator]“ unter „[2-3]“ überprüfen. Der Ordner [target] wird durch die Kompilierung (mit dem Namen build) des Projekts erzeugt. Der Ordner [classes] stellt das Stammverzeichnis des Classpath dar. Man sieht, dass der Ordner [templates] darin vorhanden ist.

1.6.5. Die Ansicht V

In MVC haben wir gerade den Controller C und das View-Modell M gesehen. Die Ansicht V wird hier durch die folgende Datei [greeting.html] dargestellt:


<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <p th:text="'Hello, ' + ${name} + '!'" />
</body>
</html>
  • Zeile 2: der Namensraum der Thymeleaf-Tags;
  • Zeile 8: ein <p>-Tag (Absatz) mit einem Thymeleaf-Attribut. Das Attribut [th:text] legt den Inhalt des Absatzes fest. Innerhalb der Zeichenkette befindet sich der Ausdruck [${name}]. Das bedeutet, dass der Wert des Attributs [name] aus der View-Vorlage abgerufen werden soll. Wir erinnern uns jedoch, dass dieses Attribut durch die folgende Aktion in die Vorlage eingefügt wurde:

model.addAttribute("name", name);

Der erste Parameter legt den Namen des Attributs fest, der zweite dessen Wert.

1.6.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);
    }

}
  • Zeile 11: Die Klasse ist mit einer für Konsolenanwendungen spezifischen Methode [main] ausführbar. Die Klasse [SpringApplication] in Zeile 12 startet den in den Abhängigkeiten enthaltenen Tomcat-Server und stellt den Webdienst darauf bereit;
  • Zeile 4: Man sieht, dass die Klasse [SpringApplication] zum Projekt [Spring Boot] gehört;
  • Zeile 12: Der erste Parameter ist die Klasse, die das Projekt konfiguriert, der zweite enthält eventuelle Parameter;
  • Zeile 8: Die Annotation [@EnableAutoConfiguration] weist Spring Boot an, die Projektkonfiguration vorzunehmen;
  • Zeile 7: Die Annotation [@ComponentScan] bewirkt, dass der Ordner, der die Klasse [Application] enthält, nach Spring-Komponenten durchsucht wird. Es wird eine gefunden, nämlich die Klasse [GreetingController], die die Annotation [@Controller] trägt, wodurch sie zu einer Spring-Komponente wird;

Führen wir das Projekt aus:

 

Es werden die folgenden Konsolenprotokolle ausgegeben:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.9.RELEASE)

2014-11-27 16:48:12.567  INFO 3908 --- [           main] hello.Application                        : Starting Application on Gportpers3 with PID 3908 (started by ST in D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete)
2014-11-27 16:48:12.723  INFO 3908 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1a38c59b: startup date [Thu Nov 27 16:48:12 CET 2014]; root of context hierarchy
2014-11-27 16:48:13.813  INFO 3908 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2014-11-27 16:48:15.247  INFO 3908 --- [           main] .t.TomcatEmbeddedServletContainerFactory : Server initialized with port: 8080
2014-11-27 16:48:15.574  INFO 3908 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2014-11-27 16:48:15.575  INFO 3908 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.56
2014-11-27 16:48:15.955  INFO 3908 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2014-11-27 16:48:15.955  INFO 3908 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3236 ms
2014-11-27 16:48:16.918  INFO 3908 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2014-11-27 16:48:16.922  INFO 3908 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2014-11-27 16:48:17.354  INFO 3908 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-27 16:48:17.679  INFO 3908 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/greeting],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String hello.GreetingController.greeting(java.lang.String,org.springframework.ui.Model)
2014-11-27 16:48:17.681  INFO 3908 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2014-11-27 16:48:17.682  INFO 3908 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2014-11-27 16:48:17.696  INFO 3908 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] auf den Handler vom Typ [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-27 16:48:17.697  INFO 3908 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto-Handler vom Typ [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-27 16:48:18.159  INFO 3908 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2014-11-27 16:48:18.491  INFO 3908 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080/http
2014-11-27 16:48:18.493  INFO 3908 --- [           main] hello.Application                        : Started Application in 6.833 seconds (JVM running for 8.658)
  • Zeile 13: Der Tomcat-Server startet auf Port 8080 (Zeile 12);
  • Zeile 17: Das Servlet [DispatcherServlet] ist vorhanden;
  • Zeile 20: Die Methode [hello.GreetingController.greeting] wurde erkannt, ebenso wie die Methode URL, die die Methode [/greeting] verarbeitet;

Um die Webanwendung zu testen, rufen wir die Methode URL und [http://localhost:8080/greeting] auf:

 

Es kann interessant sein, die vom Server gesendeten Header HTTP anzusehen. Dazu verwenden wir das Chrome-Plugin namens [Advanced Rest Client] (siehe Abschnitt 9.6):

  • in [1], das angeforderte URL;
  • Bei [2] wird die Methode GET verwendet;
  • bei [3] hat der Server angegeben, dass er eine Antwort im Format HTML sendet;
  • bei [4] die Antwort HTML;
  • in [5] wird dieselbe URL angefordert, diesmal jedoch mit einer POST;
  • in [7] werden die Informationen in der Form [urlencoded] an den Server gesendet;
  • in [6] wird der Parameter „name“ mit seinem Wert gesendet;
  • in [8] teilt der Browser dem Server mit, dass er ihm Informationen in der Form [urlencoded] sendet;
  • in [9] die Antwort HTML des Servers;

So beenden Sie die Anwendung:

1.6.7. Erstellen eines ausführbaren Archivs

Es ist möglich, ein ausführbares Archiv außerhalb von Eclipse zu erstellen. Die erforderliche Konfiguration befindet sich in der Datei [pom.xml]:


    <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>
  • Die Zeilen 7–10 definieren das Plugin, das das ausführbare Archiv erstellt;
  • Zeile 2 definiert die ausführbare Klasse des Projekts;

So geht man vor:

  • in [1]: Man führt ein Maven-Ziel aus;
  • in [2]: Es gibt zwei Ziele (Goals): [clean] zum Löschen des Ordners [target] aus dem Maven-Projekt und [package] zum erneuten Erstellen dieses Ordners;
  • in [3]: Der generierte Ordner [target] wird in diesem Ordner erstellt;
  • in [4]: Das Ziel wird generiert;

Hinweis: Damit die Generierung erfolgreich ist, muss die von STS verwendete Datei JVM eine JDK sein. [Window / Preferences / Java / Installed JREs]:

 

In den Protokollen, die in der Konsole angezeigt werden, muss unbedingt das Plugin [spring-boot-maven-plugin] erscheinen. Dieses Plugin generiert das ausführbare Archiv.

[INFO] --- spring-boot-maven-plugin:1.1.9.RELEASE:repackage (default) @ gs-serving-web-content ---

Öffnen Sie in einer Konsole den erstellten Ordner:


gs-serving-web-content-complete\target>dir
 ...

 Répertoire de D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete
\target

27/11/2014  17:07    <DIR>          .
27/11/2014  17:07    <DIR>          ..
27/11/2014  17:07    <DIR>          classes
27/11/2014  17:07    <DIR>          generated-sources
27/11/2014  17:07        13 419 551 gs-serving-web-content-0.1.0.jar
27/11/2014  17:07             3 522 gs-serving-web-content-0.1.0.jar.original
27/11/2014  17:07    <DIR>          maven-archiver
27/11/2014  17:07    <DIR>          maven-status
  • Zeile 12: das generierte Archiv;

Dieses Archiv wird wie folgt ausgeführt:


gs-serving-web-content-complete\target>java -jar gs-serving-web-content-0.1.0.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.9.RELEASE)

2014-11-27 17:14:50.439  INFO 8172 --- [           main] hello.Application                        : Starting Application on Gportpers3 with PID 8172 (D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete\target\gs-serving-web-content-0.1.0.jar started by ST in D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete\target)
2014-11-27 17:14:50.491  INFO 8172 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@12f4ec3a: startup date [Thu Nov 27 17:14:50 CET 2014]; root of context hierarchy

Hinweis: Zuvor muss der möglicherweise in Eclipse gestartete Webdienst beendet werden (siehe Seite 17).

Nachdem die Webanwendung nun gestartet ist, kann man sie mit einem Browser aufrufen:

 

1.6.8. Bereitstellung der Anwendung auf einem Tomcat-Server

Spring Boot ist zwar im Entwicklungsmodus sehr praktisch, eine Anwendung in der Produktion wird jedoch auf einem echten Tomcat-Server bereitgestellt. So gehen Sie vor:

Ändern Sie 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-serving-web-content</artifactId>
    <version>0.1.0</version>
    <packaging>war</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>

    <dependencies>
        <!-- Thymeleaf-Umgebung -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- Erstellung der WAR-Datei -->
<!--         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </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-milestone</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestone</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>

</project>

Die Änderungen müssen an zwei Stellen vorgenommen werden:

  • Zeile 9: Es muss angegeben werden, dass ein WAR-Archiv (Web ARchive) generiert werden soll;
  • Zeilen 24–28: Es muss eine Abhängigkeit zum Artefakt [spring-boot-starter-tomcat] hinzugefügt werden. Dieses Artefakt fügt alle Tomcat-Klassen in die Projektabhängigkeiten ein;
  • Zeile 27: Dieses Artefakt ist [provided], d. h., die entsprechenden Archive werden nicht in die generierte WAR-Datei aufgenommen. Diese Archive befinden sich nämlich auf dem Tomcat-Server, auf dem die Anwendung ausgeführt wird;

Wenn man sich die aktuellen Abhängigkeiten des Projekts ansieht, stellt man fest, dass die Abhängigkeit [spring-boot-starter-tomcat] bereits vorhanden ist:

  

Es besteht daher kein Grund, sie in die Datei [pom.xml] hinzuzufügen. Wir haben sie zur Erinnerung auskommentiert.

Außerdem muss die Webanwendung konfiguriert werden. Da die Datei [web.xml] fehlt, erfolgt dies über eine Klasse, die von [SpringBootServletInitializer] erbt:

  

Die Klasse [ApplicationInitializer] lautet wie folgt:


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 neu definiert (Zeile 8);
  • Zeile 10: Es wird die Klasse angegeben, die das Projekt konfiguriert;

Um das Projekt auszuführen, kann man wie folgt vorgehen:

  • in [1] wird das Projekt auf einem der in IDE Eclipse registrierten Server ausgeführt;
  • In [2] wählt man oben [Tomcat v8.0] aus;

Anschließend kann man URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] in einem Browser aufrufen:

 

Hinweis: Je nach Version von [tomcat] und [tc Server Developer] kann diese Ausführung fehlschlagen. Dies war beispielsweise bei [Apache Tomcat 8.0.3 et 8.0.15] der Fall. Oben wurde die Tomcat-Version [8.0.9] verwendet.

Wir wissen nun, wie man ein WAR-Archiv erstellt. Im weiteren Verlauf werden wir weiterhin mit Spring Boot und dessen ausführbarem JAR-Archiv arbeiten.

1.7. Ein zweites Spring-Projekt: MVC

1.7.1. Das Demonstrationsprojekt

  • in [1] importieren wir eines der Spring-Handbücher;
  • 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 der Anleitung aus;
  • in [5] bestätigen wir;
  • in [6] das importierte Projekt;

Webdienste, die über Standard-URL erreichbar sind und Text im Format jSON liefern, werden oft als REST-Dienste (REpresentational State Transfer) bezeichnet. In diesem Dokument werde ich den Dienst, den wir erstellen werden, einfach als Webdienst / jSON bezeichnen. Ein Dienst wird als RESTful bezeichnet, wenn er bestimmte Regeln einhält. Ich habe nicht versucht, diese einzuhalten.

Sehen wir uns nun das importierte Projekt an, zunächst seine Maven-Konfiguration.

1.7.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.1.9.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 Eigenschaften des Maven-Projekts. Es fehlt ein Tag [<packaging>], das den Typ der durch die Maven-Kompilierung erzeugten Datei angibt. Da dieses fehlt, wird der Typ [jar] verwendet. Die Anwendung ist also eine ausführbare Konsolenanwendung und keine Webanwendung, bei der das Packaging dann [war] lauten würde;
  • Zeilen 10–14: Das Maven-Projekt hat ein übergeordnetes Projekt mit dem Typ [spring-boot-starter-parent]. Dieses definiert den Großteil der Projektabhängigkeiten. Entweder sind diese ausreichend – in diesem Fall werden keine weiteren hinzugefügt – oder nicht, in welchem Fall die fehlenden Abhängigkeiten hinzugefügt werden;
  • Zeilen 17–20: Das Artefakt [spring-boot-starter-web] enthält die Bibliotheken, die für ein Spring-Projekt MVC vom Typ Webservice erforderlich sind, bei dem keine Ansichten generiert werden. Dieses Artefakt enthält eine sehr große Anzahl von Bibliotheken, darunter auch die eines eingebetteten Tomcat-Servers. Auf diesem Server wird die Anwendung ausgeführt;

Die von dieser Konfiguration bereitgestellten Bibliotheken sind sehr zahlreich:

Oben sind die drei Archive des Tomcat-Servers zu sehen.

1.7.3. Die Architektur eines Spring-Dienstes [web / jSON]

Zur Erinnerung: So implementiert Spring MVC das Modell MVC:

Die Bearbeitung einer Client-Anfrage läuft wie folgt ab:

  1. Anfrage – die angeforderten URL haben die Form http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... [Dispatcher Servlet] ist die Spring-Klasse, die eingehende URL verarbeitet. Sie „leitet“ die URL an die Aktion weiter, die sie verarbeiten soll. Diese Aktionen sind Methoden bestimmter Klassen, die [Contrôleurs] heißen. Das „C“ in MVC ist hier die Zeichenkette [Dispatcher Servlet, Contrôleur, Action]. Wenn keine Aktion zur Verarbeitung des eingehenden URL konfiguriert wurde, antwortet das Servlet [Dispatcher Servlet], dass das angeforderte URL nicht gefunden wurde (Fehler 404 NOT FOUND);
  2. Verarbeitung
  • Die ausgewählte Aktion kann die Parameter parami nutzen, die ihr das Servlet [Dispatcher Servlet] übermittelt hat. Diese können aus verschiedenen Quellen stammen:
    • aus dem Pfad [/param1/param2/...] des URL,
    • aus den Parametern [p1=v1&p2=v2] des URL,
    • aus Parametern, die der Browser mit seiner Anfrage übermittelt hat;
  • Bei der Bearbeitung der Benutzeranfrage benötigt die Aktion möglicherweise die Schichten [metier] und [2b]. Sobald die Anfrage des Clients bearbeitet wurde, kann dies verschiedene Antworten auslösen. Ein klassisches Beispiel ist:
    • eine Fehlerseite, wenn die Anfrage nicht korrekt verarbeitet werden konnte
    • ansonsten eine Bestätigungsseite
  • die Aktion fordert die Anzeige einer bestimmten Ansicht an: [3]. Diese Ansicht zeigt Daten an, die als Modell der Ansicht bezeichnet werden. Das ist das M in MVC. Die Aktion erstellt dieses Modell M [2c] und fordert eine Ansicht V auf, sich anzuzeigen: [3];
  1. Antwort – die ausgewählte Ansicht V verwendet die von der Aktion erstellte Vorlage M, um die dynamischen Teile der Antwort HTML zu initialisieren, die sie an den Client senden muss, und sendet diese Antwort anschließend.

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 Bibliothek jSON in die Zeichenkette jSON umgewandelt;
  • in [4b] wird diese Zeichenfolge jSON an den Browser gesendet;

1.7.4. Der Controller C

  

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, d. h., ihre Methoden werden zur Verarbeitung von URL registriert. Wir haben bereits die ähnliche Annotation [@Controller] gesehen. Das Ergebnis der Methoden dieses Controllers war ein Typ [String], der den Namen der anzuzeigenden Ansicht darstellte. Hier ist das anders. Die Methoden eines Controllers vom Typ [@RestController] geben Objekte zurück, die serialisiert werden, um an den Browser gesendet zu werden. Der Typ der durchgeführten Serialisierung hängt von der Spring-Konfiguration MVC ab. Hier werden sie als jSON serialisiert. Das Vorhandensein einer Bibliothek namens jSON in den Projektabhängigkeiten führt dazu, dass Spring Boot das Projekt durch Autokonfiguration auf diese Weise einrichtet;
  • Zeile 14: Die Annotation [@RequestMapping] gibt das URL an, das von der Methode verarbeitet wird, in diesem Fall das 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 atomare Long-Ganzzahl. Das bedeutet, dass sie parallelen Zugriff unterstützt. Mehrere Threads können gleichzeitig versuchen, die Variable [counter] zu inkrementieren. Dies erfolgt korrekt. Ein Thread kann den Wert des Zählers erst lesen, wenn der Thread, der ihn gerade ändert, seine Änderung abgeschlossen hat.

1.7.5. Das Modell M

Das durch die vorherige Methode erzeugte M-Modell ist das folgende Objekt [Greeting]:

  

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 Transformation jSON dieses Objekts erzeugt die Zeichenkette {"id":n,"content":"text"}. Letztendlich hat die von der Controller-Methode erzeugte Zeichenkette jSON folgende Form:

{"id":2,"content":"Hello, World!"}

oder

{"id":2,"content":"Hello, John!"}

1.7.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);
    }

}

Diesen Code haben wir bereits im vorherigen Beispiel kennengelernt und erläutert.

1.7.7. Ausführung des Projekts

Führen wir das Projekt aus:

 

Es werden die folgenden Konsolenprotokolle ausgegeben:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.9.RELEASE)

2014-11-28 15:22:55.005  INFO 3152 --- [           main] hello.Application                        : Starting Application on Gportpers3 with PID 3152 (started by ST in D:\data\istia-1415\spring mvc\dvp-final\gs-rest-service)
2014-11-28 15:22:55.046  INFO 3152 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@62e136d3: startup date [Fri Nov 28 15:22:55 CET 2014]; root of context hierarchy
2014-11-28 15:22:55.762  INFO 3152 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2014-11-28 15:22:56.567  INFO 3152 --- [           main] .t.TomcatEmbeddedServletContainerFactory : Server initialized with port: 8080
2014-11-28 15:22:56.738  INFO 3152 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2014-11-28 15:22:56.740  INFO 3152 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.56
2014-11-28 15:22:56.869  INFO 3152 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2014-11-28 15:22:56.870  INFO 3152 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1827 ms
2014-11-28 15:22:57.478  INFO 3152 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2014-11-28 15:22:57.481  INFO 3152 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2014-11-28 15:22:57.685  INFO 3152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-28 15:22:57.879  INFO 3152 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/greeting],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public hello.Greeting hello.GreetingController.greeting(java.lang.String)
2014-11-28 15:22:57.884  INFO 3152 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2014-11-28 15:22:57.885  INFO 3152 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2014-11-28 15:22:57.906  INFO 3152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] auf den Handler vom Typ [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-28 15:22:57.907  INFO 3152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] auf den Handler vom Typ [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-28 15:22:58.231  INFO 3152 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2014-11-28 15:22:58.318  INFO 3152 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080/http
2014-11-28 15:22:58.319  INFO 3152 --- [           main] hello.Application                        : Started Application in 3.788 seconds (JVM running for 4.424)
  • 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 erkannt;

Um die Webanwendung zu testen, rufen wir die Methode URL [http://localhost:8080/greeting] auf:

 

Man erhält tatsächlich die erwartete Zeichenfolge jSON.

Hinweis: Dieses Beispiel hat mit dem integrierten Browser von Eclipse nicht funktioniert.

Es kann interessant sein, die vom Server gesendeten Header HTTP anzusehen. Dazu verwenden wir das Chrome-Plugin namens [Advanced Rest Client] (siehe Anhang, Abschnitt 9.6):

  • in [1], das angeforderte URL;
  • in [2] wird die Methode GET verwendet;
  • in [3] die Antwort jSON;
  • in [4] hat der Server angegeben, dass er eine Antwort im Format jSON sendet;
  • in [5] wird dieselbe URL angefordert, diesmal jedoch mit einer POST;
  • In [7] werden die Informationen in der Form [urlencoded] an den Server gesendet;
  • Bei [6] wird der Parameter „name“ mit seinem Wert gesendet;
  • in [8] teilt der Browser dem Server mit, dass er ihm Informationen in der Form [urlencoded] sendet;
  • in [9] die Antwort jSON des Servers;

1.7.8. Erstellung eines ausführbaren Archivs

Wie bereits im vorherigen Projekt erstellen wir ein ausführbares Archiv:

  • in [1]: Wir führen ein Maven-Ziel aus;
  • in [2]: Es gibt zwei Ziele (Goals): [clean] zum Löschen des Ordners [target] aus dem Maven-Projekt und [package] zum erneuten Erstellen dieses Ordners;
  • in [3]: Der generierte Ordner [target] wird in diesem Ordner erstellt;
  • in [4]: Das Ziel wird generiert;

In den Protokollen, die in der Konsole angezeigt werden, muss unbedingt das Plugin [spring-boot-maven-plugin] erscheinen. Dieses Plugin generiert das ausführbare Archiv.

[INFO] --- spring-boot-maven-plugin:1.1.0.RELEASE:repackage (default) @ gs-rest-service ---

Öffnen Sie in einer Konsole den erstellten 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)
...

Hinweis: Zuvor muss der möglicherweise in Eclipse gestartete Webdienst beendet werden (siehe Abschnitt 1.6.6).

Nachdem die Webanwendung nun gestartet ist, kann man sie über einen Browser aufrufen:

 

1.7.9. Die Anwendung auf einem Tomcat-Server bereitstellen

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: Es muss angegeben werden, dass ein WAR-Archiv (Web ARchive) generiert werden soll;

Außerdem muss die Webanwendung konfiguriert werden. Da die Datei [web.xml] fehlt, erfolgt dies über eine Klasse, die von [SpringBootServletInitializer] erbt:

  

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 neu definiert (Zeile 8);
  • Zeile 10: Es wird die Klasse angegeben, die das Projekt konfiguriert;

Um das Projekt auszuführen, kann man wie folgt vorgehen:

  • In [1-2] wird das Projekt auf einem der in IDE Eclipse registrierten Server ausgeführt;

Anschließend kann man die Seite URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] in einem Browser aufrufen:

 

1.8. Conclusion

Wir haben zwei Arten von Spring-Projekten eingeführt:

  • ein Projekt, bei dem die Webanwendung einen Stream HTML an den Browser sendet. Dieser Stream wird von der View-Engine [Thymeleaf] generiert;
  • ein Projekt, bei dem die Webanwendung einen Stream jSON an den Browser sendet;

Im ersten Fall benötigt das Projekt zwei Maven-Abhängigkeiten:


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
</dependencies>

Im zweiten Fall lauten die Maven-Abhängigkeiten wie folgt:


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
</dependencies>

Die durch diese Konfigurationen ausgelösten kaskadierenden Abhängigkeiten sind sehr zahlreich, und viele davon sind unnötig. Für die Inbetriebnahme der Anwendung wird eine manuelle Maven-Konfiguration verwendet, die ausschließlich die für das Projekt erforderlichen Abhängigkeiten enthält.

Wir kehren nun zu den Grundlagen der Webprogrammierung zurück und stellen zwei grundlegende Konzepte vor:

  • den Dialog HTTP (HyperText Transfer Protocol) zwischen einem Browser und einer Webanwendung;
  • die Sprache HTML (HyperText Markup Language), die der Browser interpretiert, um eine empfangene Seite anzuzeigen;