Skip to content

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:

  1. 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.
  1. 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];
  1. 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:

  1. 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);
  2. 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];
  1. 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:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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/**] naar handler van het type [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 van het type [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)
  • 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.

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

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:

  1. 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);
  2. 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];
  1. 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:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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/**] naar handler van het type [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 [/**] naar handler van het type [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)
  • 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.

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

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;