16. Inleiding tot Spring MVC
16.1. 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 SGBD via de laag [DAO];
- De laag [DAO] (Data Access Objects), de laag [ORM] (Object Relational Mapper) en de driver JDBC regelen de toegang tot de gegevens van de 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. Een specificatie met de naam JPA (Java Persistence API) maakt het mogelijk om abstractie te maken van de gebruikte ORM, mits deze de specificaties implementeert. Dat is het geval in deze tutorial en daarom zullen we de ORM-laag voortaan de JPA-laag noemen;
- de integratie van de lagen wordt verzorgd door het Spring-framework;
16.2. Het Spring-ontwikkelingsmodel 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
- (vervolg)
- 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 samen met het verzoek zijn verzonden;
- bij de verwerking van het verzoek van de gebruiker kan de actie de laag [métier] [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.
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;
Laten we nu het verband tussen de webarchitectuur MVC en de gelaagde architectuur verduidelijken. Afhankelijk van de definitie die we aan het model geven, 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, hebben we weliswaar een webarchitectuur MVC, maar geen meerlaagse architectuur. Hier zal de laag [web] alles afhandelen: presentatie, bedrijfslogica, toegang tot gegevens. Dit werk wordt uitgevoerd door de acties.
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 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 zich rechts van de laag [Web] bevindt
- 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.
16.3. Een webproject / jSON met Spring MVC
De website [http://spring.io/guides] biedt starttutorials om het Spring-ecosysteem te ontdekken. We volgen er een om de Maven-configuratie te ontdekken die nodig is voor een Spring-project MVC.
16.3.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. Een service wordt als RESTful beschouwd als deze aan bepaalde regels voldoet.
Laten we nu het geïmporteerde project bekijken, te beginnen met de Maven-configuratie.
16.3.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.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<start-class>hello.Application</start-class>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
- 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.
16.3.3. De architectuur van een Spring-service [web / jSON]
Voor een webservice / jSON implementeert Spring MVC het model MVC als volgt:
![]() |
- 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;
16.3.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 worden 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 het 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 met de naam 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.
16.3.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:
of
16.3.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. 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 string jSON. Het kan interessant zijn om de headers HTTP te bekijken die door de server worden verzonden. Hiervoor gebruiken we de Chrome-extensie met de naam [Advanced Rest Client] (Chrome / Ctrl-T / Menu [Applications] / [Advanced Rest Client] – zie bijlagen, paragraaf 23.11):
![]() |
- in [1], de gevraagde URL;
- in [2] wordt de methode GET gebruikt;
- in [3], het antwoord jSON;
- in [4] heeft de server aangegeven 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 naar de server verzonden in de vorm van [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 jSON van de server;
16.3.7. Een uitvoerbaar archief maken
We maken nu een uitvoerbaar archief:
![]() |
![]() |
- 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, [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 genereert het uitvoerbare archief (zie [pom.xml] hieronder):
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Met een console ga je 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)
...
Nu de webapplicatie is gestart, kun je deze openen met een browser:
![]() |
16.3.8. 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 webtoepassing 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:
![]() |
16.4. Conclusion
We hebben een type Spring-project geïntroduceerd, MVC, waarbij de webapplicatie een stream jSON naar de browser verstuurt. We gaan nu een webapplicatie / jSON ontwikkelen om de database [dbproduitscategories], die in de voorgaande hoofdstukken is behandeld, op het web beschikbaar te maken.























