1. Inleiding
De PDF van dit document is beschikbaar |HIER|.
De voorbeelden in dit document zijn beschikbaar |HIER|.
We willen hier aan de hand van voorbeelden de belangrijke begrippen van Spring MVC introduceren, een Java-webframework dat een kader biedt voor het ontwikkelen van webapplicaties volgens het MVC-model (Model – View – Controller). Spring MVC is een onderdeel van het Spring-ecosysteem [http://projects.spring.io/spring-framework/]. We bespreken ook de Thymeleaf-view-engine [http://www.thymeleaf.org/].
Deze cursus is bedoeld voor lezers die de programmeertaal Java goed beheersen. Kennis van webprogrammering is niet vereist.
Hoewel dit document gedetailleerd is, is het waarschijnlijk onvolledig. Spring is een enorm framework met talrijke vertakkingen. Om Spring MVC verder te verdiepen, kunt u de volgende bronnen raadplegen:
- het referentiedocument van het Spring-framework [http://docs.spring.io/spring/docs/current/spring-framework-reference/pdf/spring-framework-reference.pdf];
- talrijke Spring-tutorials zijn te vinden op de URL [http://spring.io/guides]
- de website van [developpez.com] gewijd aan Spring [http://spring.developpez.com/].
Het document is zo geschreven dat het ook zonder computer bij de hand kan worden gelezen. Daarom zijn er veel schermafbeeldingen opgenomen.
1.1. Sources
Dit document heeft twee belangrijke bronnen:
- [Inleiding tot het ASP.NET MVC-framework aan de hand van voorbeelden (2013)]. Spring MVC en ASP.NET MVC zijn twee vergelijkbare frameworks, waarbij het tweede pas veel later is ontwikkeld dan het eerste. Om de twee frameworks te kunnen vergelijken, heb ik dezelfde opbouw gevolgd als in het document over ASP.NET en MVC;
- het document over ASP.NET MVC bevat op dit moment (dec. 2014) nog geen casestudy met de bijbehorende oplossing. Ik heb hier die uit het document [Een client/server-voorbeeld – AngularJS 1.x / Spring 4 (2014)] overgenomen en als volgt aangepast:
- de casestudy in [Tutoriel AngularJS / Spring 4] betreft een client/server-toepassing waarbij de server een webservice / jSON is, gebouwd met Spring MVC, en de client een client AngularJS;
- in dit document wordt dezelfde webservice / jSON gebruikt, maar is de client een 2-tier webapplicatie [client jQuery] / [service web / jSON];
Naast deze bronnen heb ik op internet naar antwoorden op mijn vragen gezocht. Vooral de website [http://stackoverflow.com/] is daarbij nuttig geweest.
1.2. De gebruikte tools
De volgende voorbeelden zijn getest in de volgende omgeving:
- Windows 8.1 Pro 64-bits computer;
- JDK 1.8;
- IDE Spring Tool Suite 3.6.3 (zie paragraaf 9.3);
- Chrome-browser (andere browsers zijn niet gebruikt);
- Chrome-extensie [Advanced Rest Client] (zie paragraaf 9.6);
Let op JDK 1.8. Een van de methoden in de casestudy maakt gebruik van een methode uit het [java.lang]-pakket van Java 8.
Alle voorbeelden zijn Maven-projecten die zowel in Eclipse (IDE) als in NetBeans (IntellijIDEA) kunnen worden geopend. De schermafbeeldingen hieronder zijn afkomstig uit de Spring Tool Suite (IDE), een variant van Eclipse.
1.3. De voorbeelden
De voorbeelden zijn beschikbaar op URL [http://tahe.developpez.com/java/springmvc-thymeleaf] in de vorm van een downloadbaar bestand zip.
![]() |
Om alle projecten in STS te laden, gaat u als volgt te werk:
![]() |
![]() |
- in [1-3]: importeer Maven-projecten;
![]() |
- in [4], geef de map met voorbeelden aan;
- in [5], selecteer alle projecten in de map;
- in [6], bevestig;
- in [7], de geïmporteerde projecten;
1.4. De rol van Spring MVC in een webapplicatie
Laten we Spring MVC in de context van de ontwikkeling van een webapplicatie plaatsen. Meestal wordt deze gebouwd op basis van een meerlaagse architectuur, zoals de volgende:
![]() |
- de laag [Web] is de laag die in contact staat met de gebruiker van de webapplicatie. De gebruiker communiceert met de webapplicatie via webpagina’s die in een browser worden weergegeven. Spring MVC bevindt zich in deze laag en uitsluitend in deze laag;
- de laag [métier] implementeert de bedrijfsregels van de applicatie, zoals de berekening van een salaris of een factuur. Deze laag maakt gebruik van gegevens die afkomstig zijn van de gebruiker via de laag [Web] en van de laag SGBD via de laag [DAO];
- de laag [DAO] (Data Access Objects), de laag [ORM] (Object Relational Mapper) en de driver JDBC beheren de toegang tot de gegevens van de laag SGBD. De laag [ORM] vormt een brug tussen de objecten die door de laag [DAO] worden beheerd en de rijen en kolommen van de tabellen in een relationele database. We zullen hier gebruikmaken van Hibernate (ORM). Een specificatie genaamd JPA (Java Persistence API) maakt het mogelijk om abstractie te maken van de gebruikte ORM, mits deze de specificaties implementeert. Dit is het geval bij Hibernate en andere Java-ORM'en. We zullen de ORM-laag voortaan de JPA-laag noemen;
- de integratie van de lagen wordt verzorgd door het Spring-framework;
De meeste voorbeelden die hierna worden gegeven, zullen slechts één laag gebruiken, namelijk de [Web]-laag:
![]() |
Dit document wordt echter afgesloten met de bouw van een meerlaagse webapplicatie:
![]() |
De browser maakt verbinding met een [Web1]-applicatie die is geïmplementeerd met Spring MVC / Thymeleaf, die haar gegevens ophaalt bij een webservice [Web2], eveneens geïmplementeerd met Spring MVC. Deze tweede webapplicatie heeft toegang tot een database.
1.5. Het ontwikkelingsmodel van Spring MVC
Spring MVC implementeert het zogenaamde MVC-architectuurmodel (Model – View – Controller) op de volgende manier:
![]() |
De verwerking van een verzoek van een klant verloopt als volgt:
- verzoek – de aangevraagde URL hebben de vorm http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... De [Front Controller] gebruikt een configuratiebestand of Java-annotaties om het verzoek naar de juiste controller en de juiste actie binnen die controller te „routeren“. Hiervoor gebruikt het het veld [Action] van de URL. De rest van de URL [/param1/param2/...] bestaat uit optionele parameters die aan de actie worden doorgegeven. De C van MVC is hier de tekenreeks [Front Controller, Contrôleur, Action]. Als geen enkele controller de gevraagde actie kan verwerken, zal de webserver antwoorden dat de gevraagde URL niet is gevonden.
- verwerking
- De gekozen actie kan gebruikmaken van de parameters parami die de [Front Controller] aan haar heeft doorgegeven. Deze kunnen uit verschillende bronnen afkomstig zijn:
- het pad [/param1/param2/...] van de URL,
- de parameters [p1=v1&p2=v2] van de URL,
- van parameters die door de browser bij het verzoek zijn meegestuurd;
- bij de verwerking van het verzoek van de gebruiker kan de actie de lagen [métier] en [2b] nodig hebben. Zodra het verzoek van de klant is verwerkt, kan dit verschillende reacties oproepen. Een klassiek voorbeeld is:
- een foutpagina als het verzoek niet correct kon worden verwerkt
- een bevestigingspagina in het andere geval
- de actie vraagt om een bepaalde weergave [3] weer te geven. Deze weergave toont gegevens die het model van de weergave worden genoemd. Dit is de M van MVC. De actie zal dit model M [2c] aanmaken en vragen om een weergave V weer te geven [3];
- antwoord – de gekozen weergave V gebruikt het door de actie opgebouwde model M om de dynamische delen van het antwoord HTML te initialiseren dat zij naar de client moet verzenden, en verstuurt vervolgens dit antwoord.
Laten we nu het verband tussen webarchitectuur MVC en gelaagde architectuur verduidelijken. Afhankelijk van de definitie die men aan het model geeft, zijn deze twee concepten al dan niet met elkaar verbonden. Laten we een eengelaagde Spring-webapplicatie MVC nemen:
![]() |
Als we de laag [Web] implementeren met Spring MVC, krijgen we weliswaar een webarchitectuur MVC, maar geen meerlaagse architectuur. Hier zal de laag [web] alles voor zijn rekening nemen: presentatie, bedrijfslogica, toegang tot gegevens. Het zijn de acties die dit werk zullen doen.
Laten we nu eens kijken naar een meerlaagse webarchitectuur:
![]() |
De laag [Web] kan worden geïmplementeerd zonder framework en zonder het model MVC te volgen. We hebben dan wel een meerlaagse architectuur, maar de weblaag implementeert het model MVC niet.
Bijvoorbeeld: in de .NET-omgeving kan de hierboven genoemde laag [Web]bovenstaande laag worden geïmplementeerd met ASP.NET en MVC, waardoor we een gelaagde architectuur krijgen met een [Web]-laag van het type MVC. Zodra dit is gebeurd, kan deze laag ASP.NET MVC worden vervangen door een klassieke laag ASP.NET (WebForms), terwijl de rest (bedrijfslaag, DAO, ORM) ongewijzigd. We hebben dan een gelaagde architectuur met een laag [Web] die niet langer van het type MVC is.
In MVC hebben we gezegd dat het model M dat van de weergave V is, c.a.d. De verzameling gegevens die door de weergave V wordt weergegeven. Er wordt een andere definitie van het model M van MVC gegeven:
![]() |
Veel auteurs zijn van mening dat wat zich rechts van de laag [Web] bevindt, het model M van MVC vormt. Om dubbelzinnigheden te voorkomen, kan men spreken van:
- het domeinmodel wanneer men alles bedoelt wat rechts van de laag [Web]
- het model van de weergave wanneer we verwijzen naar de gegevens die door een weergave V worden getoond
In het vervolg zal de term „M-model” uitsluitend verwijzen naar het model van een weergave V.
1.6. Een eerste Spring-project MVC
Vanaf nu werken we met de IDE Spring Tool Suite (STS), een voor Spring aangepaste variant van Eclipse. De website [http://spring.io/guides] biedt tutorials om aan de slag te gaan en het Spring-ecosysteem te ontdekken. We gaan een van deze tutorials volgen om de Maven-configuratie te ontdekken die nodig is voor een Spring-project MVC.
Opmerking: de details van het project zullen voor de meeste beginners onbegrijpelijk zijn. Dat is niet belangrijk. Deze details worden verderop in dit document uitgelegd. We beperken ons tot het naspelen van de stappen.
1.6.1. Het demonstratieproject
![]() |
- in [1] importeren we een van de Spring-gidsen;
![]() |
- in [2], kiezen we het voorbeeld [Serving Web Content];
- in [3] kiezen we het Maven-project;
- in [4] kiezen we de definitieve versie van de handleiding;
- in [5], valideren we;
- in [6] het geïmporteerde project;
Laten we het project eens bekijken, te beginnen met de Maven-configuratie.
1.6.2. Maven-configuratie
Het bestand [pom.xml] ziet er als volgt uit:
<?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>
- regels 6-8: de eigenschappen van het Maven-project. Er ontbreekt een tag [<packaging>] die het type aangeeft van het bestand dat door de Maven-compilatie wordt geproduceerd. Bij gebrek hieraan wordt het type [jar] gebruikt. De applicatie is dus een uitvoerbaar consoleprogramma, en geen webapplicatie, waarvoor de packaging dan [war] zou zijn;
- regels 10-14: het Maven-project heeft een bovenliggend project met de naam [spring-boot-starter-parent]. Dit bovenliggende project definieert het grootste deel van de afhankelijkheden van het project. Deze kunnen voldoende zijn, in welk geval er geen extra afhankelijkheden worden toegevoegd, of onvoldoende, in welk geval de ontbrekende afhankelijkheden worden toegevoegd;
- regels 17-20: het artefact [spring-boot-starter-thymeleaf] bevat de bibliotheken die nodig zijn voor een Spring-project MVC dat samen met een view-engine genaamd [Thymeleaf] wordt gebruikt. Dit artefact bevat een zeer groot aantal bibliotheken, waaronder die van een ingebouwde Tomcat-server. Op deze server zal de applicatie worden uitgevoerd;
De bibliotheken die bij deze configuratie worden meegeleverd, zijn zeer talrijk:
![]() | ![]() |
Hierboven ziet u de archieven van de Tomcat-server.
Spring Boot is een onderdeel van het Spring-ecosysteem [http://projects.spring.io/spring-boot/]. Dit project heeft tot doel de configuratie van Spring-projecten zo veel mogelijk te beperken. Hiervoor voert Spring Boot automatische configuratie uit op basis van de afhankelijkheden die in het classpath van het project aanwezig zijn. Spring Boot biedt talrijke kant-en-klare afhankelijkheden. Zo zorgt de afhankelijkheid [spring-boot-starter-thymeleaf], die in het vorige Maven-project werd aangetroffen, voor alle benodigde afhankelijkheden voor een Spring-applicatie MVC die gebruikmaakt van de view-engine [Thymeleaf]. Met deze twee kenmerken:
- kant-en-klare afhankelijkheden;
- automatische configuratie op basis van deze afhankelijkheden en ‘redelijke’ standaardwaarden, kun je heel snel een operationele Spring-applicatie MVC opzetten. Dat is het geval bij het project dat hier wordt besproken;
1.6.3. De architectuur van een Spring-applicatie MVC
Spring MVC implementeert het zogenaamde MVC-architectuurmodel (Model – View – Controller):
![]() |
De verwerking van een verzoek van een klant verloopt als volgt:
- verzoek – de aangevraagde URL hebben de vorm http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... De [Dispatcher Servlet] is de Spring-klasse die de binnenkomende URL verwerkt. Deze „routeert“ de URL naar de actie die deze moet verwerken. Deze acties zijn methoden van specifieke klassen die [Contrôleurs] worden genoemd. De C van MVC is hier de tekenreeks [Dispatcher Servlet, Contrôleur, Action]. Als er geen actie is geconfigureerd om de binnenkomende URL te verwerken, zal de servlet [Dispatcher Servlet] antwoorden dat de gevraagde URL niet is gevonden (fout 404 NOT FOUND);
- verwerking
- de gekozen actie kan gebruikmaken van de parameters parami die de servlet [Dispatcher Servlet] aan haar heeft doorgegeven. Deze kunnen uit verschillende bronnen afkomstig zijn:
- het pad [/param1/param2/...] van de URL,
- de parameters [p1=v1&p2=v2] van de URL,
- parameters die door de browser bij het verzoek zijn meegestuurd;
- bij de verwerking van het verzoek van de gebruiker kan de actie de laag [metier] [2b] nodig hebben. Zodra het verzoek van de klant is verwerkt, kan dit verschillende reacties oproepen. Een klassiek voorbeeld is:
- een foutpagina als het verzoek niet correct kon worden verwerkt
- een bevestigingspagina in het andere geval
- de actie vraagt om een bepaalde weergave [3] weer te geven. Deze weergave toont gegevens die het weergavemodel worden genoemd. Dit is de M van MVC. De actie zal dit model M [2c] aanmaken en vragen om een weergave V weer te geven [3];
- antwoord – de gekozen weergave V gebruikt het door de actie aangemaakte model M om de dynamische delen van het antwoord HTML te initialiseren dat zij naar de client moet sturen, en verstuurt vervolgens dit antwoord.
We gaan deze verschillende elementen in het onderzochte project bekijken.
1.6.4. De controller C
![]() |
De geïmporteerde applicatie heeft de volgende 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";
}
}
- regel 8: de annotatie [@Controller] maakt van de klasse [GreetingController] een Spring-controller, d.w.z. dat de methoden ervan zijn geregistreerd om URL te verwerken. Een Spring-controller is een singleton. Er wordt slechts één exemplaar van aangemaakt;
- regel 11: de annotatie [@RequestMapping] geeft aan welk URL door de methode wordt verwerkt, in dit geval het URL [/greeting]. We zullen later zien dat deze URL kan worden geconfigureerd en dat het mogelijk is om deze parameters op te halen;
- regel 12: de methode accepteert twee parameters:
- [String name]: deze parameter wordt geïnitialiseerd door een parameter met de naam [name] in de verwerkte aanvraag, bijvoorbeeld [/greeting?name=alfonse]. Deze parameter is optioneel ([required=false]) en wanneer deze ontbreekt, krijgt de parameter [name] de waarde 'World' ([defaultValue="World"]),
- [Model model] is een weergavesjabloon. Het wordt leeg aangeleverd en het is de taak van de actie (de methode greeting) om het in te vullen. Dit sjabloon wordt doorgegeven aan de weergave die door de actie wordt weergegeven. Het is dus een weergavesjabloon;
- regel 13: de waarde van [name] wordt in het weergavemodel geplaatst. De klasse [Model] gedraagt zich als een woordenboek;
- regel 14: de methode retourneert de naam van de weergave die het opgebouwde model moet weergeven. De exacte naam van de weergave hangt af van de configuratie van [Thymeleaf]. Als deze ontbreekt, wordt hier de weergave [/templates/greeting.html] weergegeven of moet de map [templates] zich in de root van het classpath van het project bevinden;
Laten we ons Eclipse-project eens bekijken:
![]() |
De mappen [src/main/java] en [src/main/resources] zijn beide mappen waarvan de inhoud in het classpath van het project wordt geplaatst. Voor [src/main/java] zijn dit de gecompileerde versies van de Java-broncode. De inhoud van de map [src/main/resources] wordt daarentegen zonder wijzigingen in het classpath geplaatst. We zien dus dat de map [templates] in het classpath van het project [1] zal staan.
Dit kan worden gecontroleerd in het venster [Navigator] van Eclipse [Window / Show view / Other / General / Navigator]. De map [target] wordt aangemaakt door de compilatie (genaamd build) van het project. De map [classes] vormt de root van het Classpath. We zien dat de map [templates] daar aanwezig is.
1.6.5. De weergave V
In MVC hebben we zojuist de controller C en het view-model M gezien. De view V wordt hier vertegenwoordigd door het volgende bestand [greeting.html]:
<!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>
- regel 2: de naamruimte van de Thymeleaf-tags;
- regel 8: een <p>-tag (alinea) met een Thymeleaf-attribuut. Het attribuut [th:text] bepaalt de inhoud van de alinea. Binnen de tekenreeks staat de uitdrukking [${name}]. Dit betekent dat we de waarde van het attribuut [name] uit het view-sjabloon willen hebben. We herinneren ons echter dat dit attribuut in het sjabloon is geplaatst door de actie:
model.addAttribute("name", name);
De eerste parameter bepaalt de naam van het attribuut, de tweede de waarde ervan.
1.6.6. Uitvoering
![]() |
De klasse [Application.java] is de uitvoerbare klasse van het project. De code ervan is als volgt:
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);
}
}
- regel 11: de klasse is uitvoerbaar met een methode [main] die specifiek is voor console-applicaties. De klasse [SpringApplication] op regel 12 start de Tomcat-server die in de afhankelijkheden staat en implementeert de webservice daarop;
- regel 4: we zien dat de klasse [SpringApplication] behoort tot het project [Spring Boot];
- regel 12: de eerste parameter is de klasse die het project configureert, de tweede eventuele parameters;
- regel 8: de annotatie [@EnableAutoConfiguration] vraagt Spring Boot om het project te configureren;
- regel 7: de annotatie [@ComponentScan] zorgt ervoor dat de map die de klasse [Application] bevat, wordt doorzocht naar Spring-componenten. Er wordt er één gevonden: de klasse [GreetingController], die de annotatie [@Controller] heeft, waardoor het een Spring-component is;
Laten we het project uitvoeren:
![]() |
We krijgen de volgende console-logs:
- regel 13: de Tomcat-server start op poort 8080 (regel 12);
- regel 17: de servlet [DispatcherServlet] is aanwezig;
- regel 20: de methode [hello.GreetingController.greeting] is gedetecteerd, evenals de URL die de [/greeting] verwerkt;
Om de webapplicatie te testen, roepen we de URL en [http://localhost:8080/greeting] op:
![]() | ![]() |
Het kan interessant zijn om de door de server verzonden headers HTTP te bekijken. Hiervoor gebruiken we de Chrome-plug-in met de naam [Advanced Rest Client] (zie paragraaf 9.6):
![]() |
- in [1], de aangevraagde URL;
- in [2] wordt de methode GET gebruikt;
- in [3] heeft de server aangegeven dat hij een antwoord in het formaat HTML verstuurt;
- in [4], het antwoord HTML;
- in [5] wordt hetzelfde URL opgevraagd, maar ditmaal met een POST;
- in [7] wordt de informatie naar de server verzonden in de vorm [urlencoded];
- in [6], de parameter name met zijn waarde;
- in [8] geeft de browser aan de server door dat hij informatie verstuurt in de vorm van [urlencoded];
- in [9], het antwoord HTML van de server;
Om de toepassing te stoppen:
![]() | ![]() | ![]() |
1.6.7. Een uitvoerbaar archief aanmaken
Het is mogelijk om buiten Eclipse een uitvoerbaar archief aan te maken. De benodigde configuratie staat in het bestand [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>
- de regels 7-10 definiëren de plug-in die het uitvoerbare archief gaat aanmaken;
- regel 2 definieert de uitvoerbare klasse van het project;
Dit gaat als volgt:
![]() |
- in [1]: we voeren een Maven-doel uit;
![]() |
- naar [2]: er zijn twee doelen (goals): [clean] om de map [target] uit het Maven-project te verwijderen, [package] om deze opnieuw te genereren;
- in [3]: de gegenereerde map [target] wordt in deze map aangemaakt;
- in [4]: het doel wordt gegenereerd;
Opmerking: om de generatie te laten slagen, moet de JVM die door STS wordt gebruikt, een JDK [Window / Preferences / Java / Installed JREs] zijn:
![]() |
In de logbestanden die in de console verschijnen, is het belangrijk dat de plug-in [spring-boot-maven-plugin] wordt weergegeven. Deze zorgt voor het genereren van het uitvoerbare archief.
Met een console ga je naar de gegenereerde map:
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
- regel 12: het gegenereerde archief;
Dit archief wordt als volgt uitgevoerd:
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
Opmerking: je moet eerst de webservice stoppen die mogelijk in Eclipse is gestart (zie pagina 17).
Nu de webapplicatie is gestart, kan deze via een browser worden opgeroepen:
![]() |
1.6.8. De applicatie op een Tomcat-server implementeren
Hoewel Spring Boot erg handig is in de ontwikkelingsmodus, wordt een applicatie in productie op een echte Tomcat-server geïmplementeerd. Dit gaat als volgt:
Pas het bestand [pom.xml] als volgt aan:
<?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-omgeving -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- genereren van de WAR -->
<!-- <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>
De wijzigingen moeten op twee plaatsen worden aangebracht:
- regel 9: hier moet worden aangegeven dat er een WAR-archief (Web ARchive) wordt gegenereerd;
- regels 24-28: voeg een afhankelijkheid toe voor het artefact [spring-boot-starter-tomcat]. Dit artefact voegt alle Tomcat-klassen toe aan de afhankelijkheden van het project;
- regel 27: dit artefact is [provided], wat betekent dat de bijbehorende archieven niet in de gegenereerde WAR worden opgenomen. Deze archieven zijn namelijk te vinden op de Tomcat-server waarop de applicatie zal draaien;
Als we de huidige afhankelijkheden van het project bekijken, zien we dat de afhankelijkheid [spring-boot-starter-tomcat] al aanwezig is:
![]() |
Het is dus niet nodig om deze toe te voegen aan het bestand [pom.xml]. We hebben deze ter herinnering uitgecommentarieerd.
Verder moet de webapplicatie worden geconfigureerd. Aangezien het bestand [web.xml] ontbreekt, gebeurt dit met een klasse die afstamt van [SpringBootServletInitializer]:
![]() |
De klasse [ApplicationInitializer] ziet er als volgt uit:
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);
}
}
- regel 6: de klasse [ApplicationInitializer] is een uitbreiding van de klasse [SpringBootServletInitializer];
- regel 9: de methode [configure] wordt opnieuw gedefinieerd (regel 8);
- regel 10: de klasse die het project configureert, wordt opgegeven;
Om het project uit te voeren, kunt u als volgt te werk gaan:
![]() |
- in [1] voert men het project uit op een van de servers die zijn geregistreerd in de IDE Eclipse;
- in [2] kiest men hierboven [Tomcat v8.0];
Zodra dit is gebeurd, kan men de URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] in een browser opvragen:
![]() |
Opmerking: afhankelijk van de versies van [tomcat] en [tc Server Developer] kan deze uitvoering mislukken. Dit was bijvoorbeeld het geval met [Apache Tomcat 8.0.3 et 8.0.15]. Hierboven werd de Tomcat-versie [8.0.9] gebruikt.
We weten nu hoe we een WAR-archief moeten genereren. Vervolgens gaan we verder met Spring Boot en het uitvoerbare JAR-archief daarvan.
1.7. Een tweede Spring-project: MVC
1.7.1. Het demonstratieproject
![]() |
- in [1] importeren we een van de Spring-handleidingen;
![]() |
- in [2] kiezen we het voorbeeld [Rest Service];
- in [3] kiezen we het Maven-project;
- in [4] kiezen we de definitieve versie van de handleiding;
- in [5], valideren we;
- in [6]: het geïmporteerde project;
Webservices die via standaard URL toegankelijk zijn en jSON-tekst leveren, worden vaak REST-services (REpresentational State Transfer) genoemd. In dit document zal ik de service die we gaan bouwen simpelweg een / jSON-webservice noemen. Een service wordt als RESTful beschouwd als deze aan bepaalde regels voldoet. Ik heb niet geprobeerd om aan deze regels te voldoen.
Laten we nu het geïmporteerde project bekijken, te beginnen met de Maven-configuratie.
1.7.2. Maven-configuratie
Het bestand [pom.xml] ziet er als volgt uit:
<?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>
- regels 6-8: de eigenschappen van het Maven-project. Er ontbreekt een tag [<packaging>] die het type aangeeft van het bestand dat door de Maven-compilatie wordt geproduceerd. Bij gebrek hieraan wordt het type [jar] gebruikt. De applicatie is dus een uitvoerbaar consoleprogramma, en geen webapplicatie, waarbij de packaging dan [war] zou zijn;
- regels 10-14: het Maven-project heeft een bovenliggend project met de naam [spring-boot-starter-parent]. Dit project definieert het grootste deel van de afhankelijkheden van het project. Deze kunnen voldoende zijn, in welk geval er geen extra afhankelijkheden worden toegevoegd, of onvoldoende, in welk geval de ontbrekende afhankelijkheden worden toegevoegd;
- regels 17-20: het artefact [spring-boot-starter-web] bevat de bibliotheken die nodig zijn voor een Spring-project MVC van het type webservice, waarbij geen weergaven worden gegenereerd. Dit artefact bevat een zeer groot aantal bibliotheken, waaronder die van een ingebouwde Tomcat-server. Op deze server zal de applicatie worden uitgevoerd;
De bibliotheken die bij deze configuratie worden meegeleverd, zijn zeer talrijk:
![]() | ![]() |
Hierboven ziet u de drie archiefbestanden van de Tomcat-server.
1.7.3. De architectuur van een Spring-service [web / jSON]
Laten we nog eens bekijken hoe Spring MVC het model MVC implementeert:
![]() |
De verwerking van een verzoek van een client verloopt als volgt:
- verzoek – de aangevraagde URL-verzoeken hebben de vorm http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... De [Dispatcher Servlet] is de Spring-klasse die de binnenkomende URL-verzoeken verwerkt. Deze „routeert“ de URL naar de actie die deze moet verwerken. Deze acties zijn methoden van specifieke klassen die [Contrôleurs] worden genoemd. De C van MVC is hier de tekenreeks [Dispatcher Servlet, Contrôleur, Action]. Als er geen actie is geconfigureerd om de binnenkomende URL te verwerken, zal de servlet [Dispatcher Servlet] antwoorden dat de gevraagde URL niet is gevonden (fout 404 NOT FOUND);
- verwerking
- de gekozen actie kan gebruikmaken van de parameters parami die de servlet [Dispatcher Servlet] aan haar heeft doorgegeven. Deze kunnen uit verschillende bronnen afkomstig zijn:
- het pad [/param1/param2/...] van de URL,
- de parameters [p1=v1&p2=v2] van de URL,
- van parameters die door de browser bij het verzoek zijn meegestuurd;
- bij de verwerking van het verzoek van de gebruiker kan de actie de laag [metier] [2b] nodig hebben. Zodra het verzoek van de klant is verwerkt, kan dit verschillende reacties oproepen. Een klassiek voorbeeld is:
- een foutpagina als het verzoek niet correct kon worden verwerkt
- een bevestigingspagina in het andere geval
- de actie vraagt om een bepaalde weergave [3] weer te geven. Deze weergave toont gegevens die het weergavemodel worden genoemd. Dit is de M van MVC. De actie zal dit model M [2c] aanmaken en vragen om een weergave V weer te geven [3];
- antwoord – de gekozen weergave V gebruikt het door de actie opgebouwde model M om de dynamische delen van het antwoord HTML te initialiseren dat zij naar de client moet verzenden, en verstuurt vervolgens dit antwoord.
Voor een webservice / jSON is de voorgaande architectuur enigszins aangepast:
![]() |
- in [4a] wordt het model, dat een Java-klasse is, door een bibliotheek jSON omgezet in de tekenreeks jSON;
- in [4b] wordt deze tekenreeks jSON naar de browser verzonden;
1.7.4. De C-controller
![]() |
De geïmporteerde applicatie heeft de volgende 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));
}
}
- regel 9: de annotatie [@RestController] maakt van de klasse [GreetingController] een Spring-controller, d.w.z. dat de methoden ervan zijn geregistreerd om URL te verwerken. We hebben de vergelijkbare annotatie [@Controller] al gezien. Het resultaat van de methoden van die controller was een type [String], wat de naam was van de weer te geven weergave. Hier is dat anders. De methoden van een controller van het type [@RestController] retourneren objecten die worden geserialiseerd om naar de browser te worden verzonden. Het type serialisatie dat wordt toegepast, hangt af van de configuratie van Spring MVC. Hier worden ze geserialiseerd naar jSON. Het is de aanwezigheid van een bibliotheek jSON in de afhankelijkheden van het project die ervoor zorgt dat Spring Boot het project via autoconfiguratie op deze manier instelt;
- regel 14: de annotatie [@RequestMapping] geeft aan welk URL door de methode wordt verwerkt, in dit geval het URL [/greeting];
- regel 15: we hebben de annotatie [@RequestParam] al uitgelegd. Het resultaat dat door de methode wordt geretourneerd, is een object van het type [Greeting].
- regel 12: een long-geheelgetal van het type ‘atomic’. Dit betekent dat het gelijktijdige toegang ondersteunt. Meerdere threads kunnen tegelijkertijd de variabele [counter] willen verhogen. Dit verloopt op een veilige manier. Een thread kan de waarde van de teller pas lezen als de thread die deze aan het wijzigen is, zijn wijziging heeft voltooid.
1.7.5. Het M-model
Het M-model dat door de vorige methode wordt gegenereerd, is het volgende object [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;
}
}
De transformatie jSON van dit object zal de tekenreeks {"id":n,"content":"tekst"} genereren. Uiteindelijk zal de tekenreeks jSON die door de methode van de controller wordt gegenereerd, de volgende vorm hebben:
{"id":2,"content":"Hello, World!"}
of
{"id":2,"content":"Hello, John!"}
1.7.6. Uitvoering
![]() |
De klasse [Application.java] is de uitvoerbare klasse van het project. De code ervan is als volgt:
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);
}
}
We zijn deze code al tegengekomen en hebben deze uitgelegd in het vorige voorbeeld.
1.7.7. Het project uitvoeren
Laten we het project uitvoeren:
![]() |
We krijgen de volgende console-logs:
- regel 13: de Tomcat-server start op poort 8080 (regel 12);
- regel 17: de servlet [DispatcherServlet] is aanwezig;
- regel 20: de methode [GreetingController.greeting] is gedetecteerd;
Om de webapplicatie te testen, roepen we de URL [http://localhost:8080/greeting] op:
![]() | ![]() |
We ontvangen inderdaad de verwachte tekenreeks jSON.
Opmerking: dit voorbeeld werkte niet met de ingebouwde browser van Eclipse.
Het kan interessant zijn om de door de server verzonden headers HTTP te bekijken. Hiervoor gebruiken we de Chrome-plug-in met de naam [Advanced Rest Client] (zie bijlagen, paragraaf 9.6):
![]() |
- in [1], de aangevraagde URL;
- in [2] wordt de methode GET gebruikt;
- in [3], het antwoord jSON;
- in [4] gaf de server aan dat hij een antwoord in het formaat jSON verstuurde;
- in [5] wordt hetzelfde URL opgevraagd, maar ditmaal met een POST;
- in [7] wordt de informatie in de vorm [urlencoded] naar de server verzonden;
- in [6], de parameter name met zijn waarde;
- in [8] geeft de browser aan de server door dat hij informatie verstuurt in de vorm van [urlencoded];
- in [9], het antwoord jSON van de server;
1.7.8. Een uitvoerbaar archief maken
Net als bij het vorige project maken we een uitvoerbaar archief aan:
![]() |
![]() |
- in [1]: we voeren een Maven-doel uit;
- in [2]: er zijn twee doelen (goals): [clean] om de map [target] uit het Maven-project te verwijderen, en [package] om deze opnieuw te genereren;
- in [3]: de gegenereerde map [target] wordt in deze map geplaatst;
- in [4]: het doel wordt gegenereerd;
In de logbestanden die in de console verschijnen, is het belangrijk dat de plug-in [spring-boot-maven-plugin] wordt weergegeven. Deze zorgt voor het genereren van het uitvoerbare archief.
Met een console gaan we naar de gegenereerde map:
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
...
- regel 5: het gegenereerde archief;
Dit archief wordt als volgt uitgevoerd:
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)
...
Opmerking: u moet eerst de webservice stoppen die mogelijk in Eclipse is gestart (zie paragraaf 1.6.6).
Nu de webapplicatie is gestart, kunnen we deze via een browser opvragen:
![]() |
1.7.9. De applicatie op een Tomcat-server implementeren
Net als bij het vorige project passen we het bestand [pom.xml] als volgt aan:
<?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>
- regel 9: hier moet worden aangegeven dat er een WAR-archief (Web ARchive) wordt gegenereerd;
Daarnaast moet de webapplicatie worden geconfigureerd. Aangezien het bestand [web.xml] ontbreekt, gebeurt dit met een klasse die afstamt van [SpringBootServletInitializer]:
![]() |
De klasse [ApplicationInitializer] ziet er als volgt uit:
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);
}
}
- regel 6: de klasse [ApplicationInitializer] is een uitbreiding van de klasse [SpringBootServletInitializer];
- regel 9: de methode [configure] wordt opnieuw gedefinieerd (regel 8);
- regel 10: de klasse die het project configureert, wordt opgegeven;
Om het project uit te voeren, kunt u als volgt te werk gaan:
![]() |
- in [1-2] voert u het project uit op een van de servers die zijn geregistreerd in de IDE Eclipse;
Zodra dit is gebeurd, kun je de URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] in een browser opvragen:
![]() |
1.8. Conclusion
We hebben twee soorten Spring-projecten geïntroduceerd:
- een project waarbij de webapplicatie een stream HTML naar de browser stuurt. Deze stream wordt gegenereerd door de view-engine [Thymeleaf];
- een project waarbij de webapplicatie een stream jSON naar de browser stuurt;
In het eerste geval zijn twee Maven-afhankelijkheden nodig voor het project:
<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>
In het tweede geval zijn de Maven-afhankelijkheden als volgt:
<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>
De afhankelijkheden die door deze configuraties in een cascade worden meegebracht, zijn zeer talrijk en veel ervan zijn overbodig. Voor de ingebruikname van de applicatie zullen we een handmatige Maven-configuratie gebruiken waarin alleen de voor het project noodzakelijke afhankelijkheden aanwezig zijn.
We gaan nu terug naar de basis van het webprogrammeren door twee basisbegrippen te introduceren:
- de communicatie via het HTTP (HyperText Transfer Protocol) tussen een browser en een webapplicatie;
- de taal HTML (HyperText Markup Language) die de browser interpreteert om een ontvangen pagina weer te geven;


















































