Skip to content

1. Introduzione

Il codice PDF di questo documento è disponibile |QUI|.

Gli esempi di questo documento sono disponibili |QUI|.

In questa sede ci proponiamo di introdurre, con l’ausilio di esempi, i concetti fondamentali di Spring MVC, un framework Web Java che fornisce un contesto per lo sviluppo di applicazioni Web secondo il modello MVC (Modello – Vista – Controller). Spring MVC è un ramo dell’ecosistema Spring [http://projects.spring.io/spring-framework/]. Presenteremo inoltre il motore di visualizzazione Thymeleaf [http://www.thymeleaf.org/].

Questo corso è rivolto a lettori che abbiano una solida padronanza del linguaggio Java. Non è necessario conoscere la programmazione web.

Sebbene dettagliato, questo documento è probabilmente incompleto. Spring è un framework vastissimo con numerose ramificazioni. Per approfondire Spring MVC, è possibile consultare i seguenti riferimenti:

  • il documento di riferimento del framework Spring [http://docs.spring.io/spring/docs/current/spring-framework-reference/pdf/spring-framework-reference.pdf];
  • numerosi tutorial su Spring sono disponibili su URL [http://spring.io/guides]
  • il sito di [developpez.com] dedicato a Spring [http://spring.developpez.com/].

Il documento è stato redatto in modo tale da poter essere letto anche senza un computer a portata di mano. Per questo motivo sono state inserite numerose schermate.

1.1. Sources

Questo documento ha due fonti principali:

  • [Introduzione al framework ASP.NET MVC attraverso esempi (2013)]. Spring MVC e ASP.NET MVC sono due framework simili, il secondo dei quali è stato sviluppato molto tempo dopo il primo. Per poter confrontare i due framework, ho seguito lo stesso schema utilizzato nel documento su ASP.NET e MVC;
  • il documento su ASP.NET MVC non contiene al momento (dicembre 2014) alcun caso di studio con la relativa soluzione. Ho ripreso qui quello del documento [Un esempio client/server - AngularJS 1.x / Spring 4 (2014)], che ho modificato come segue:
    • il caso di studio in [Tutoriel AngularJS / Spring 4] riguarda un’applicazione client/server in cui il server è un servizio web / jSON realizzato con Spring MVC e il client è un client AngularJS;
    • in questo documento si riprende lo stesso servizio web / jSON, ma il client è un'applicazione web a due livelli [client jQuery] / [service web / jSON];

Oltre a queste fonti, ho cercato su Internet le risposte alle mie domande. In particolare, mi è stato utile il sito [http://stackoverflow.com/].

1.2. Gli strumenti utilizzati

Gli esempi che seguono sono stati testati nel seguente ambiente:

  • computer con Windows 8.1 Pro a 64 bit;
  • JDK 1.8;
  • IDE Spring Tool Suite 3.6.3 (cfr. paragrafo 9.3);
  • browser Chrome (non sono stati utilizzati altri browser);
  • estensione Chrome [Advanced Rest Client] (cfr. paragrafo 9.6);

Attenzione a JDK 1.8. Uno dei metodi del caso di studio utilizza un metodo del pacchetto [java.lang] di Java 8.

Tutti gli esempi sono progetti Maven che possono essere aperti indifferentemente con IDE Eclipse, IntellijIDEA o NetBeans. Di seguito, le schermate provengono da IDE Spring Tool Suite, una variante di Eclipse.

1.3. Gli esempi

Gli esempi sono disponibili all'indirizzo URL [http://tahe.developpez.com/java/springmvc-thymeleaf] sotto forma di un file zip da scaricare.

  

Per caricare tutti i progetti in STS, procedere come segue:

  • in [1-3], importare i progetti Maven;
  • in [4], specificare la cartella degli esempi;
  • in [5], selezionate tutti i progetti nella cartella;
  • in [6], confermare;
  • in [7], i progetti importati;

1.4. Il ruolo di Spring MVC in un'applicazione web

Inquadriamo Spring MVC nello sviluppo di un’applicazione web. Nella maggior parte dei casi, questa sarà costruita su un’architettura a più livelli come la seguente:

  • il livello [Web] è il livello a contatto con l’utente dell’applicazione web. Quest’ultimo interagisce con l’applicazione web attraverso pagine web visualizzate da un browser. È in questo livello che si trova Spring MVC ed esclusivamente in questo livello;
  • il livello [métier] implementa le regole di gestione dell’applicazione, come il calcolo di uno stipendio o di una fattura. Questo livello utilizza i dati provenienti dall’utente tramite il livello [Web] e quelli provenienti da SGBD tramite il livello [DAO];
  • il livello [DAO] (Data Access Objects), il livello [ORM] (Object Relational Mapper) e il driver JDBC gestiscono l’accesso ai dati del livello SGBD. Il livello [ORM] funge da ponte tra gli oggetti gestiti dal livello [DAO] e le righe e le colonne delle tabelle di un database relazionale. In questo contesto utilizzeremo Hibernate (ORM). Una specifica denominata JPA (Java Persistence API) consente di astrarsi dall’ORM utilizzato, purché quest’ultimo implementi tali specifiche. È il caso di Hibernate e degli altri ORM Java. D'ora in poi chiameremo quindi il livello ORM, il livello JPA;
  • l’integrazione dei livelli è gestita dal framework Spring;

La maggior parte degli esempi riportati di seguito utilizzerà un solo livello, il livello [Web]:

Questo documento si concluderà tuttavia con la realizzazione di un’applicazione web multilivello:

Il browser si collegherà a un'applicazione [Web1] implementata da Spring MVC / Thymeleaf che recupererà i propri dati da un servizio web [Web2], anch’esso implementato con Spring MVC. Questa seconda applicazione web accederà a un database.

1.5. Il modello di sviluppo di Spring MVC

Spring MVC implementa il modello di architettura denominato MVC (Modello – Vista – Controller) nel modo seguente:

L’elaborazione di una richiesta da parte di un cliente avviene nel modo seguente:

  1. richiesta – le URL richieste hanno la forma http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... Il [Front Controller] utilizza un file di configurazione o annotazioni Java per «instradare» la richiesta verso il controller corretto e l’azione corretta all’interno di tale controller. A tal fine, utilizza il campo [Action] del URL. Il resto del URL [/param1/param2/...] è costituito da parametri facoltativi che saranno trasmessi all’azione. Il valore C di MVC è in questo caso la stringa [Front Controller, Contrôleur, Action]. Se nessun controller è in grado di elaborare l’azione richiesta, il server Web risponderà che l’URL richiesta non è stata trovata.
  1. Elaborazione
  • L'azione selezionata può utilizzare i parametri parami che le sono stati trasmessi da [Front Controller]. Questi possono provenire da diverse fonti:
    • dal percorso [/param1/param2/...] dell’URL,
    • dai parametri [p1=v1&p2=v2] dell'URL,
    • dai parametri inviati dal browser insieme alla sua richiesta;
  • nell'elaborazione della richiesta dell'utente, l'azione potrebbe richiedere il livello [métier] [2b]. Una volta elaborata la richiesta del cliente, questa può generare diverse risposte. Un esempio classico è:
    • una pagina di errore se la richiesta non è stata elaborata correttamente
    • una pagina di conferma in caso contrario
  • l’azione richiede la visualizzazione di una determinata vista [3]. Questa vista visualizzerà i dati denominati modello della vista. È la M di MVC. L’azione creerà questo modello M [2c] e richiederà la visualizzazione di una vista V [3];
  1. risposta: la vista V selezionata utilizza il modello M creato dall’azione per inizializzare le parti dinamiche della risposta HTML che deve inviare al client, quindi invia tale risposta.

Ora precisiamo il legame tra l’architettura web MVC e l’architettura a livelli. A seconda della definizione che si dà al modello, questi due concetti sono collegati oppure no. Prendiamo un’applicazione web Spring MVC a un unico livello:

Se implementiamo il livello [Web] con Spring MVC, avremo sì un’architettura web MVC, ma non un’architettura a più livelli. In questo caso, il livello [web] si occuperà di tutto: presentazione, logica di business, accesso ai dati. Saranno le azioni a svolgere questo lavoro.

Consideriamo ora un'architettura web multistrato:

Il livello [Web] può essere implementato senza framework e senza seguire il modello MVC. Si ha quindi effettivamente un’architettura multistrato, ma il livello Web non implementa il modello MVC.

Ad esempio, nell’ambiente .NET, il livello [Web] quisopra può essere implementato con ASP.NET e MVC, ottenendo così un’architettura a livelli con un livello [Web] di tipo MVC. Fatto ciò, è possibile sostituire questo livello ASP.NET MVC con un livello ASP.NET classico (WebForms), mantenendo il resto (di business, DAO, ORM) invariato. Si ottiene così un’architettura a livelli con un livello [Web] che non è più di tipo MVC.

In MVC, abbiamo affermato che il modello M era quello della vista V, c.a.d, ovvero l’insieme dei dati visualizzati dalla vista V. Viene fornita un’altra definizione del modello M di MVC:

Molti autori ritengono che ciò che si trova a destra del livello [Web] costituisca il modello M del MVC. Per evitare ambiguità, si può parlare:

  • del modello di dominio quando si indica tutto ciò che si trova a destra del livello [Web]
  • del modello della vista quando si indicano i dati visualizzati da una vista V

Di seguito, il termine «modello M» indicherà esclusivamente il modello di una vista V.

1.6. Un primo progetto Spring MVC

D'ora in poi lavoreremo con la Spring Tool Suite (IDE), una variante di Eclipse personalizzata per Spring. Il sito [http://spring.io/guides] offre tutorial introduttivi per scoprire l’ecosistema Spring. Ne seguiremo uno per scoprire la configurazione Maven necessaria per un progetto Spring MVC.

Nota: la comprensione dei dettagli del progetto sfuggirà alla maggior parte dei principianti. Non è importante. Questi dettagli vengono spiegati nel prosieguo del documento. Ci limiteremo a riprodurre le operazioni.

1.6.1. Il progetto dimostrativo

  • in [1], importiamo una delle guide Spring;
  • in [2], scegliamo l’esempio [Serving Web Content];
  • in [3], si seleziona il progetto Maven;
  • in [4], si seleziona la versione finale della guida;
  • in [5], si conferma;
  • in [6], il progetto importato;

Esaminiamo il progetto, iniziando dalla sua configurazione Maven.

1.6.2. Configurazione Maven

Il file [pom.xml] è il seguente:


<?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>
  • righe 6-8: le proprietà del progetto Maven. Manca un tag [<packaging>] che indichi il tipo di file prodotto dalla compilazione Maven. In assenza di tale tag, viene utilizzato il tipo [jar]. L’applicazione è quindi un’applicazione eseguibile di tipo console, e non un’applicazione web, nel qual caso il packaging sarebbe [war];
  • righe 10-14: il progetto Maven ha un progetto padre [spring-boot-starter-parent]. È questo che definisce la maggior parte delle dipendenze del progetto. Queste possono essere sufficienti, nel qual caso non se ne aggiungono altre, oppure no, nel qual caso si aggiungono le dipendenze mancanti;
  • righe 17-20: l'artefatto [spring-boot-starter-thymeleaf] include le librerie necessarie per un progetto Spring MVC utilizzato in combinazione con un motore di viste denominato [Thymeleaf]. Questo artefatto include un numero molto elevato di librerie, tra cui quelle di un server Tomcat integrato. È su questo server che verrà eseguita l’applicazione;

Le librerie incluse in questa configurazione sono molto numerose:

Sopra sono visibili gli archivi del server Tomcat.

Spring Boot è un ramo dell’ecosistema Spring [http://projects.spring.io/spring-boot/]. Questo progetto mira a ridurre al minimo la configurazione dei progetti Spring. A tal fine, Spring Boot esegue l’autoconfigurazione sulla base delle dipendenze presenti nel Classpath del progetto. Spring Boot fornisce numerose dipendenze pronte all’uso. Pertanto, la dipendenza [spring-boot-starter-thymeleaf] presente nel precedente progetto Maven include tutte le dipendenze necessarie per un’applicazione Spring MVC che utilizza il motore di visualizzazione [Thymeleaf]. Grazie a queste due caratteristiche:

  • dipendenze pronte all’uso;
  • autoconfigurazione basata su queste dipendenze e su valori predefiniti «ragionevoli», è possibile ottenere molto rapidamente un'applicazione Spring MVC operativa. È il caso del progetto qui esaminato;

1.6.3. L’architettura di un’applicazione Spring MVC

Spring MVC implementa il modello architettonico denominato MVC (Modello – Vista – Controller):

L’elaborazione di una richiesta da parte di un cliente avviene nel modo seguente:

  1. richiesta - le URL richieste hanno la forma http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... La [Dispatcher Servlet] è la classe di Spring che gestisce le URL in entrata. Essa "insedia" l'URL verso l'azione che deve elaborarla. Queste azioni sono metodi di classi specifiche denominate [Contrôleurs]. La C di MVC è in questo caso la stringa [Dispatcher Servlet, Contrôleur, Action]. Se non è stata configurata alcuna azione per elaborare il URL in entrata, il servlet [Dispatcher Servlet] risponderà che il URL richiesto non è stato trovato (errore 404 NOT FOUND);
  2. elaborazione
  • l'azione selezionata può utilizzare i parametri parami che il servlet [Dispatcher Servlet] le ha trasmesso. Questi possono provenire da diverse fonti:
    • dal percorso [/param1/param2/...] del URL,
    • dai parametri [p1=v1&p2=v2] del URL,
    • dei parametri inviati dal browser insieme alla sua richiesta;
  • nell'elaborazione della richiesta dell'utente, l'azione potrebbe richiedere il livello [metier] [2b]. Una volta elaborata la richiesta del cliente, questa può generare diverse risposte. Un esempio classico è:
    • una pagina di errore se la richiesta non è stata elaborata correttamente
    • una pagina di conferma in caso contrario
  • l’azione richiede la visualizzazione di una determinata vista [3]. Questa vista visualizzerà i dati denominati modello della vista. È la M di MVC. L’azione creerà questo modello M [2c] e richiederà la visualizzazione di una vista V [3];
  1. risposta: la vista V selezionata utilizza il modello M creato dall’azione per inizializzare le parti dinamiche della risposta HTML che deve inviare al client, quindi invia tale risposta.

Esamineremo questi diversi elementi nel progetto in esame.

1.6.4. Il controller C

  

L’applicazione importata presenta il seguente 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";
    }

}
  • riga 8: l'annotazione [@Controller] rende la classe [GreetingController] un controller Spring, ovvero i suoi metodi sono registrati per gestire i URL. Un controller Spring è un singleton. Ne viene creata una sola istanza;
  • riga 11: l'annotazione [@RequestMapping] indica l'URL che il metodo gestisce, in questo caso l'URL [/greeting]. Vedremo in seguito che questo URL può essere configurato e che è possibile recuperare tali parametri;
  • riga 12: il metodo accetta due parametri:
    • [String name]: questo parametro viene inizializzato da un parametro denominato [name] nella richiesta elaborata, ad esempio [/greeting?name=alfonse]. Questo parametro è facoltativo ([required=false]) e, quando non è presente, il parametro [name] assumerà il valore 'World' ([defaultValue="World"]),
    • [Model model] è un modello di vista. Viene fornito vuoto ed è compito dell’azione (il metodo greeting) compilarlo. È questo modello che verrà trasmesso alla vista che l’azione provvederà a visualizzare. Si tratta quindi di un modello di vista;
  • riga 13: il valore di [name] viene inserito nel modello della vista. La classe [Model] si comporta come un dizionario;
  • riga 14: il metodo restituisce il nome della vista che deve visualizzare il modello costruito. Il nome esatto della vista dipende dalla configurazione di [Thymeleaf]. In assenza di tale configurazione, la vista visualizzata qui sarà la vista [/templates/greeting.html] oppure la cartella [templates] deve trovarsi alla radice del Classpath del progetto;

Esaminiamo il nostro progetto Eclipse:

Le cartelle [src/main/java] e [src/main/resources] sono entrambe cartelle il cui contenuto verrà inserito nel Classpath del progetto. Per [src/main/java] vi saranno inserite le versioni compilate dei sorgenti Java. Il contenuto della cartella [src/main/resources] viene invece inserito nel Classpath senza modifiche. Si nota quindi che la cartella [templates] sarà presente nel Classpath del progetto [1].

È possibile verificarlo nella finestra [Navigator] di Eclipse [Window / Show view / Other / General / Navigator]. La cartella [target] viene generata dalla compilazione (denominata build) del progetto. La cartella [classes] rappresenta la radice del Classpath. Si nota che la cartella [templates] è presente al suo interno.

1.6.5. La vista V

Nel file MVC abbiamo appena visto il controller C e il modello di vista M. La vista V è qui rappresentata dal seguente file [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>
  • riga 2: lo spazio dei nomi dei tag Thymeleaf;
  • riga 8: un tag <p> (paragrafo) con un attributo Thymeleaf. L’attributo [th:text] definisce il contenuto del paragrafo. All’interno della stringa di caratteri è presente l’espressione [${name}]. Ciò significa che si desidera il valore dell’attributo [name] del modello della vista. Ricordiamo però che tale attributo è stato inserito nel modello dall’azione:

model.addAttribute("name", name);

Il primo parametro specifica il nome dell’attributo, il secondo il suo valore.

1.6.6. Esecuzione

  

La classe [Application.java] è la classe eseguibile del progetto. Il suo codice è il seguente:


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

}
  • riga 11: la classe è eseguibile con un metodo [main] specifico per le applicazioni da console. La classe [SpringApplication] alla riga 12 avvierà il server Tomcat presente nelle dipendenze e vi distribuirà il servizio web;
  • riga 4: si nota che la classe [SpringApplication] appartiene al progetto [Spring Boot];
  • riga 12: il primo parametro è la classe che configura il progetto, il secondo eventuali parametri;
  • riga 8: l’annotazione [@EnableAutoConfiguration] richiede a Spring Boot di configurare il progetto;
  • riga 7: l'annotazione [@ComponentScan] fa sì che la cartella contenente la classe [Application] venga analizzata alla ricerca dei componenti Spring. Ne verrà individuato uno, ovvero la classe [GreetingController], che presenta l'annotazione [@Controller] che la rende un componente Spring;

Eseguiamo il progetto:

 

Si ottengono i seguenti log della console:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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/**] su handler di tipo [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 di tipo [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)
  • riga 13: il server Tomcat si avvia sulla porta 8080 (riga 12);
  • riga 17: è presente il servlet [DispatcherServlet];
  • riga 20: è stato individuato il metodo [hello.GreetingController.greeting], così come il metodo URL che gestisce il metodo [/greeting];

Per testare l’applicazione web, si richiedono i metodi URL e [http://localhost:8080/greeting]:

 

Potrebbe essere interessante dare un'occhiata alle intestazioni HTTP inviate dal server. A tal fine, utilizzeremo il plugin di Chrome denominato [Advanced Rest Client] (cfr. paragrafo 9.6):

  • in [1], l'URL richiesto;
  • in [2], viene utilizzato il metodo GET;
  • in [3], il server ha indicato che avrebbe inviato una risposta nel formato HTML;
  • in [4], la risposta è HTML;
  • in [5], viene richiesta la stessa URL, ma questa volta con un POST;
  • in [7], le informazioni vengono inviate al server nella forma [urlencoded];
  • in [6], il parametro name con il suo valore;
  • in [8], il browser indica al server che gli sta inviando le informazioni [urlencoded];
  • in [9], la risposta HTML del server;

Per arrestare l'applicazione:

1.6.7. Creazione di un archivio eseguibile

È possibile creare un archivio eseguibile al di fuori di Eclipse. La configurazione necessaria si trova nel file [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>
  • le righe 7-10 definiscono il plugin che creerà l'archivio eseguibile;
  • la riga 2 definisce la classe eseguibile del progetto;

Si procede come segue:

  • in [1]: si esegue un target Maven;
  • in [2]: ci sono due obiettivi (goals): [clean] per eliminare la cartella [target] dal progetto Maven, [package] per rigenerarla;
  • in [3]: la cartella [target] generata verrà creata in questa cartella;
  • in [4]: si genera il target;

Nota: affinché la generazione vada a buon fine, è necessario che il JVM utilizzato da STS sia un JDK [Window / Preferences / Java / Installed JREs]:

 

Nei log che compaiono nella console, è importante che compaia il plugin [spring-boot-maven-plugin]. È proprio questo che genera l'archivio eseguibile.

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

Utilizzando la console, ci si posiziona nella cartella generata:


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
  • riga 12: l'archivio generato;

Questo archivio viene eseguito nel modo seguente:


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

Nota: è necessario prima arrestare il servizio web eventualmente avviato in Eclipse (cfr. pagina 17).

Ora che l'applicazione web è in esecuzione, è possibile accedervi tramite un browser:

 

1.6.8. Distribuire l'applicazione su un server Tomcat

Sebbene Spring Boot risulti molto pratico in modalità di sviluppo, un'applicazione in produzione verrà distribuita su un vero server Tomcat. Ecco come procedere:

Modificare il file [pom.xml] come segue:


<?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>
        <!-- ambiente Thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- generazione del WAR -->
<!--         <dipendenza>
            <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>

Le modifiche vanno apportate in due punti:

  • riga 9: occorre specificare che verrà generato un archivio WAR (Web ARchive);
  • righe 24-28: occorre aggiungere una dipendenza dall’artefatto [spring-boot-starter-tomcat]. Questo artefatto include tutte le classi di Tomcat nelle dipendenze del progetto;
  • riga 27: questo artefatto è [provided], ovvero gli archivi corrispondenti non saranno inseriti nel WAR generato. Infatti, tali archivi si troveranno sul server Tomcat su cui verrà eseguita l’applicazione;

In effetti, se si osservano le attuali dipendenze del progetto, si nota che la dipendenza [spring-boot-starter-tomcat] è già presente:

  

Non è quindi necessario aggiungerla al file [pom.xml]. L'abbiamo commentata a titolo di promemoria.

È inoltre necessario configurare l’applicazione web. In assenza del file [web.xml], ciò avviene tramite una classe che eredita da [SpringBootServletInitializer]:

  

La classe [ApplicationInitializer] è la seguente:


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

}
  • riga 6: la classe [ApplicationInitializer] estende la classe [SpringBootServletInitializer];
  • riga 9: il metodo [configure] viene ridefinito (riga 8);
  • riga 10: viene specificata la classe che configura il progetto;

Per eseguire il progetto, è possibile procedere come segue:

  • in [1], si esegue il progetto su uno dei server registrati in IDE Eclipse;
  • in [2], si seleziona [Tomcat v8.0];

Fatto ciò, è possibile richiamare URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] in un browser:

 

Nota: a seconda delle versioni di [tomcat] e [tc Server Developer], questa esecuzione potrebbe non andare a buon fine. È successo, ad esempio, con la versione [Apache Tomcat 8.0.3 et 8.0.15]. Nel caso sopra descritto, la versione di Tomcat utilizzata era la [8.0.9].

Ora sappiamo come generare un archivio WAR. In seguito, continueremo a lavorare con Spring Boot e il suo archivio JAR eseguibile.

1.7. Un secondo progetto Spring MVC

1.7.1. Il progetto dimostrativo

  • in [1], importiamo una delle guide di Spring;
  • in [2], scegliamo l'esempio [Rest Service];
  • in [3], si seleziona il progetto Maven;
  • in [4], si seleziona la versione finale della guida;
  • in [5], si conferma;
  • in [6], il progetto importato;

I servizi web accessibili tramite URL standard e che restituiscono testo jSON sono spesso denominati servizi REST (REpresentational State Transfer). In questo documento mi limiterò a chiamare il servizio che realizzeremo «servizio web / jSON». Un servizio è detto RESTful se rispetta determinate regole. Non ho cercato di rispettarle.

Esaminiamo ora il progetto importato, iniziando dalla sua configurazione Maven.

1.7.2. Configurazione Maven

Il file [pom.xml] è il seguente:


<?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>
  • righe 6-8: le proprietà del progetto Maven. Manca un tag [<packaging>] che indichi il tipo di file prodotto dalla compilazione Maven. In assenza di questo, viene utilizzato il tipo [jar]. L’applicazione è quindi un’applicazione eseguibile di tipo console, e non un’applicazione web, nel qual caso il packaging sarebbe [war];
  • righe 10-14: il progetto Maven ha un progetto padre [spring-boot-starter-parent]. È questo che definisce la maggior parte delle dipendenze del progetto. Queste possono essere sufficienti, nel qual caso non se ne aggiungono altre, oppure no, nel qual caso si aggiungono le dipendenze mancanti;
  • righe 17-20: l’artefatto [spring-boot-starter-web] include le librerie necessarie per un progetto Spring MVC di tipo servizio web in cui non sono presenti viste generate. Questo artefatto include un numero molto elevato di librerie, tra cui quelle di un server Tomcat integrato. È su questo server che verrà eseguita l’applicazione;

Le librerie incluse in questa configurazione sono molto numerose:

Sopra sono visibili i tre archivi del server Tomcat.

1.7.3. L’architettura di un servizio Spring [web / jSON]

Ricordiamo come Spring MVC implementi il modello MVC:

L'elaborazione di una richiesta da parte di un client avviene nel modo seguente:

  1. richiesta - le URL richieste hanno la forma http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... La [Dispatcher Servlet] è la classe di Spring che gestisce le URL in entrata. Essa "insedia" l'URL verso l'azione che deve elaborarla. Queste azioni sono metodi di classi specifiche denominate [Contrôleurs]. La C di MVC è in questo caso la stringa [Dispatcher Servlet, Contrôleur, Action]. Se non è stata configurata alcuna azione per gestire l’URL in entrata, il servlet [Dispatcher Servlet] risponderà che l’URL richiesto non è stato trovato (errore 404 NOT FOUND);
  2. elaborazione
  • l'azione selezionata può utilizzare i parametri parami che il servlet [Dispatcher Servlet] le ha trasmesso. Questi possono provenire da diverse fonti:
    • dal percorso [/param1/param2/...] del URL,
    • dai parametri [p1=v1&p2=v2] del URL,
    • dai parametri inviati dal browser insieme alla sua richiesta;
  • nell'elaborazione della richiesta dell'utente, l'azione potrebbe richiedere il livello [metier] [2b]. Una volta elaborata la richiesta del cliente, questa può generare diverse risposte. Un esempio classico è:
    • una pagina di errore se la richiesta non è stata elaborata correttamente
    • una pagina di conferma in caso contrario
  • l’azione richiede la visualizzazione di una determinata vista [3]. Questa vista visualizzerà i dati denominati modello della vista. È la M di MVC. L’azione creerà questo modello M [2c] e richiederà la visualizzazione di una vista V [3];
  1. risposta: la vista V selezionata utilizza il modello M creato dall’azione per inizializzare le parti dinamiche della risposta HTML che deve inviare al client, quindi invia tale risposta.

Per un servizio web / jSON, l’architettura precedente viene leggermente modificata:

  • in [4a], il modello, che è una classe Java, viene trasformato in una stringa jSON da una libreria jSON;
  • in [4b], questa stringa jSON viene inviata al browser;

1.7.4. Il controller C

  

L'applicazione importata presenta il seguente 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));
    }
}
  • riga 9: l'annotazione [@RestController] trasforma la classe [GreetingController] in un controller Spring, ovvero i suoi metodi vengono registrati per gestire gli oggetti URL. Abbiamo già visto l'annotazione simile [@Controller]. Il risultato dei metodi di quel controller era un tipo [String], che corrispondeva al nome della vista da visualizzare. Qui è diverso. I metodi di un controller di tipo [@RestController] restituiscono oggetti che vengono serializzati per essere inviati al browser. Il tipo di serializzazione effettuata dipende dalla configurazione di Spring MVC. In questo caso, saranno serializzati in jSON. È la presenza di una libreria jSON tra le dipendenze del progetto che fa sì che Spring Boot, tramite autoconfigurazione, configuri il progetto in questo modo;
  • riga 14: l’annotazione [@RequestMapping] indica l’URL su cui opera il metodo, in questo caso l’URL [/greeting];
  • riga 15: abbiamo già spiegato l’annotazione [@RequestParam]. Il risultato restituito dal metodo è un oggetto di tipo [Greeting].
  • riga 12: un intero lungo di tipo atomico. Ciò significa che supporta la concorrenza di accesso. Diversi thread potrebbero voler incrementare la variabile [counter] contemporaneamente. Ciò avverrà in modo corretto. Un thread può leggere il valore del contatore solo se il thread che lo sta modificando ha completato la modifica.

1.7.5. Il modello M

Il modello M generato dal metodo precedente è il seguente oggetto [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;
    }
}

La trasformazione jSON di questo oggetto creerà la stringa {"id":n,"content":"testo"}. Alla fine, la stringa jSON generata dal metodo del controller avrà la forma:

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

oppure

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

1.7.6. Esecuzione

  

La classe [Application.java] è la classe eseguibile del progetto. Il suo codice è il seguente:


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

}

Abbiamo già visto e spiegato questo codice nell'esempio precedente.

1.7.7. Esecuzione del progetto

Eseguiamo il progetto:

 

Si ottengono i seguenti log della console:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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/**] su handler di tipo [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 [/**] su handler di tipo [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)
  • riga 13: il server Tomcat si avvia sulla porta 8080 (riga 12);
  • riga 17: è presente il servlet [DispatcherServlet];
  • riga 20: il metodo [GreetingController.greeting] è stato individuato;

Per testare l'applicazione web, si richiede l'URL [http://localhost:8080/greeting]:

 

Si riceve correttamente la stringa jSON prevista.

Nota: questo esempio non ha funzionato con il browser integrato di Eclipse.

Potrebbe essere interessante osservare le intestazioni HTTP inviate dal server. A tal fine, utilizzeremo il plugin di Chrome denominato [Advanced Rest Client] (cfr. Allegati, paragrafo 9.6):

  • in [1], l’URL richiesto;
  • in [2], viene utilizzato il metodo GET;
  • in [3], la risposta jSON;
  • in [4], il server ha indicato che avrebbe inviato una risposta nel formato jSON;
  • in [5], viene richiesta la stessa URL, ma questa volta con un POST;
  • in [7], le informazioni vengono inviate al server nella forma [urlencoded];
  • in [6], il parametro name con il suo valore;
  • in [8], il browser indica al server che gli sta inviando le informazioni [urlencoded];
  • in [9], la risposta jSON del server;

1.7.8. Creazione di un archivio eseguibile

Come abbiamo fatto per il progetto precedente, creiamo un archivio eseguibile:

  • in [1]: si esegue un target Maven;
  • in [2]: ci sono due obiettivi (goals): [clean] per eliminare la cartella [target] dal progetto Maven, [package] per rigenerarla;
  • in [3]: la cartella [target] generata verrà creata in questa cartella;
  • in [4]: si genera il target;

Nei log che compaiono nella console, è importante che compaia il plugin [spring-boot-maven-plugin]. È proprio questo che genera l'archivio eseguibile.

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

Utilizzando una console, ci si posiziona nella cartella generata:


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
...
  • riga 5: l'archivio generato;

Questo archivio viene eseguito nel modo seguente:


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)
...

Nota: è necessario arrestare preventivamente il servizio web eventualmente avviato in Eclipse (cfr. paragrafo 1.6.6).

Ora che l'applicazione web è stata avviata, è possibile accedervi tramite un browser:

 

1.7.9. Distribuire l'applicazione su un server Tomcat

Come fatto per il progetto precedente, modifichiamo il file [pom.xml] nel modo seguente:


<?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>
  • riga 9: occorre specificare che verrà generato un archivio WAR (Web ARchive);

È inoltre necessario configurare l’applicazione web. In assenza del file [web.xml], ciò avviene tramite una classe che eredita da [SpringBootServletInitializer]:

  

La classe [ApplicationInitializer] è la seguente:


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

}
  • riga 6: la classe [ApplicationInitializer] estende la classe [SpringBootServletInitializer];
  • riga 9: il metodo [configure] viene ridefinito (riga 8);
  • riga 10: viene specificata la classe che configura il progetto;

Per eseguire il progetto, è possibile procedere come segue:

  • in [1-2], si esegue il progetto su uno dei server registrati in IDE Eclipse;

Fatto ciò, è possibile richiamare l'URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] in un browser:

 

1.8. Conclusion

Abbiamo introdotto due tipi di progetti Spring MVC:

  • un progetto in cui l’applicazione web invia un flusso HTML al browser. Questo flusso è generato dal motore di visualizzazione [Thymeleaf];
  • un progetto in cui l’applicazione web invia un flusso jSON al browser;

Nel primo caso, il progetto richiede due dipendenze Maven:


    <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>

Nel secondo caso, le dipendenze Maven sono le seguenti:


    <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>

Le dipendenze a cascata generate da queste configurazioni sono molto numerose e molte sono superflue. Per la messa in produzione dell’applicazione, si utilizzerà una configurazione Maven manuale in cui saranno presenti solo le dipendenze necessarie al progetto.

Torneremo ora alle basi della programmazione web presentando due concetti fondamentali:

  • il dialogo HTTP (HyperText Transfer Protocol) tra un browser e un'applicazione web;
  • il linguaggio HTML (HyperText Markup Language) che il browser interpreta per visualizzare una pagina che ha ricevuto;