Skip to content

1. Introduzione

Il PDF di questo documento è disponibile |QUI|.

Gli esempi contenuti in questo documento sono disponibili |QUI|.

In questa sede, intendiamo introdurre, attraverso alcuni esempi, i concetti chiave di Spring MVC, un framework web Java che fornisce un'infrastruttura per lo sviluppo di applicazioni web secondo il modello MVC (Model–View–Controller). Spring MVC è un ramo dell'ecosistema Spring [http://projects.spring.io/spring-framework/]. Presentiamo inoltre il motore di visualizzazione Thymeleaf [http://www.thymeleaf.org/].

Questo corso è rivolto a lettori con una solida padronanza del linguaggio Java. Non è richiesta alcuna conoscenza preliminare di programmazione web.

Sebbene dettagliato, questo documento è probabilmente incompleto. Spring è un framework vasto con molti rami. Per saperne di più su Spring MVC, è possibile consultare le seguenti risorse:

Questo documento è stato scritto in modo da poter essere letto senza un computer a portata di mano. Pertanto, sono incluse molte schermate.

1.1. Fonti

Questo documento ha due fonti principali:

  • [Introduzione ad ASP.NET MVC con esempi]. Spring MVC e ASP.NET MVC sono due framework simili, il secondo dei quali è stato sviluppato molto tempo dopo il primo. Per confrontare i due framework, ho seguito la stessa progressione del documento su ASP.NET MVC;
  • il documento su ASP.NET MVC attualmente (dicembre 2014) non contiene un caso di studio con la relativa soluzione. Ho utilizzato quello del documento [Tutorial su AngularJS / Spring 4], che ho modificato come segue:
    • Il caso di studio in [Tutorial 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, utilizziamo lo stesso servizio web/JSON, ma il client è un'applicazione web a due livelli [client jQuery] / [servizio web/JSON];

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

1.2. Strumenti utilizzati

I seguenti esempi 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 (vedere la Sezione 9.3);
  • Browser Chrome (non sono stati utilizzati altri browser);
  • Estensione Chrome [Advanced Rest Client] (vedere la sezione 9.6);

Nota relativa a JDK 1.8: uno dei metodi nel caso di studio utilizza un metodo del pacchetto [java.lang] in Java 8.

Tutti gli esempi sono progetti Maven che possono essere aperti in Eclipse, IntelliJ IDEA o NetBeans. Di seguito, le schermate provengono dall'IDE Spring Tool Suite, una variante di Eclipse.

1.3. Gli esempi

Gli esempi sono disponibili |QUI| come file ZIP scaricabile.

  

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], selezionare 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. Molto spesso, essa sarà costruita su un'architettura a più livelli come la seguente:

  • il livello [Web] è quello a contatto con l'utente dell'applicazione web. L'utente interagisce con l'applicazione web attraverso le pagine web visualizzate in un browser. Spring MVC si trova in questo livello e solo in questo livello;
  • il livello [business] implementa la logica di business 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 dal DBMS 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 nel DBMS. Il livello [ORM] funge da ponte tra gli oggetti gestiti dal livello [DAO] e le righe e le colonne delle tabelle in un database relazionale. Qui useremo l'ORM Hibernate. Una specifica denominata JPA (Java Persistence API) ci permette di astrarre dall'ORM specifico utilizzato, a condizione che esso implementi queste specifiche. Questo è il caso di Hibernate e di altri ORM Java. D'ora in poi ci riferiremo quindi al livello ORM come al livello JPA;
  • l'integrazione dei livelli è gestita dal framework Spring;

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

Tuttavia, questo documento si concluderà con la realizzazione di un'applicazione web multilivello:

Il browser si collegherà a un'applicazione [Web1] implementata utilizzando 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 Spring MVC

Spring MVC implementa il modello architetturale MVC (Model–View–Controller) come segue:

L'elaborazione di una richiesta del client procede come segue:

  1. richiesta - gli URL richiesti 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 al controller corretto e all'azione corretta all'interno di quel controller. Per farlo, utilizza il campo [Action] dell'URL. Il resto dell'URL [/param1/param2/...] è costituito da parametri opzionali che verranno passati all'azione. La C in MVC qui si riferisce alla catena [Front Controller, Controller, Action]. Se nessun controller è in grado di gestire l'azione richiesta, il server web risponderà che l'URL richiesto non è stato trovato.
  2. elaborazione
  • L'azione selezionata può utilizzare i parametri che il [Front Controller] le ha passato. Questi possono provenire da diverse fonti:
    • il percorso [/param1/param2/...] dell'URL,
    • i parametri [p1=v1&p2=v2] dell'URL,
    • dai parametri inviati dal browser con la sua richiesta;
  • durante l'elaborazione della richiesta dell'utente, l'azione potrebbe aver bisogno del livello [business] [2b]. Una volta elaborata la richiesta del client, questa può innescare varie risposte. Un esempio classico è:
    • una pagina di errore se la richiesta non è stata elaborata correttamente
    • una pagina di conferma in caso contrario
  • L'azione istruisce una vista specifica a renderizzare [3]. Questa vista visualizzerà i dati noti come modello di vista. Questa è la "M" in MVC. L'azione creerà questo modello di vista [2c] e istruirà una vista a renderizzare [3];
  1. Risposta - la vista selezionata V utilizza il modello M costruito dall'azione per inizializzare le parti dinamiche della risposta HTML che deve inviare al client, quindi invia questa risposta.

Ora, chiariamo la relazione tra l'architettura web MVC e l'architettura a livelli. A seconda di come definiamo il modello, questi due concetti possono essere correlati o meno. Consideriamo un'applicazione web Spring MVC a singolo livello:

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

Ora, consideriamo un'architettura web multistrato:

Il livello [Web] può essere implementato senza un framework e senza seguire il modello MVC. Abbiamo quindi un'architettura a più livelli, ma il livello Web non implementa il modello MVC.

Ad esempio, nell'ambiente .NET, il livello [Web] sopra descritto può essere implementato con ASP.NET MVC, ottenendo un'architettura a livelli con un livello [Web] in stile MVC. Una volta fatto ciò, possiamo sostituire questo livello ASP.NET MVC con un livello ASP.NET classico (WebForms) mantenendo il resto (logica di business, DAO, ORM) invariato. Otterremo così un'architettura a livelli con un livello [Web] che non è più basato su MVC.

In MVC, abbiamo detto che il modello M era quello della vista V, ovvero l'insieme di dati visualizzati dalla vista V. Viene fornita un'altra definizione del modello M in MVC:

Molti autori ritengono che ciò che si trova a destra del livello [Web] costituisca il modello M dell'MVC. Per evitare ambiguità, possiamo fare riferimento al:

  • il modello di dominio quando ci si riferisce a tutto ciò che si trova a destra del livello [Web]
  • il modello di vista quando ci si riferisce ai dati visualizzati da una vista V

D'ora in poi, il termine "modello M" si riferirà esclusivamente al modello di una vista V.

1.6. Un primo progetto Spring MVC

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

Nota: la maggior parte dei principianti non comprenderà appieno i dettagli del progetto. Non è importante. Questi dettagli vengono spiegati più avanti in questo documento. Seguiremo semplicemente i passaggi.

1.6.1. Il progetto demo

  • In [1], importiamo una delle guide di Spring;
  • in [2], selezioniamo l'esempio [Serving Web Content];
  • in [3], selezioniamo il progetto Maven;
  • in [4], selezioniamo la versione finale della guida;
  • in [5], confermiamo;
  • 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 specifichi il tipo di file prodotto dalla build di Maven. In sua assenza, viene utilizzato il tipo [jar]. L'applicazione è quindi un'applicazione eseguibile basata su console, non un'applicazione web, dove il packaging sarebbe [war];
  • righe 10–14: il progetto Maven ha un progetto padre [spring-boot-starter-parent]. Questo definisce la maggior parte delle dipendenze del progetto. Esse potrebbero essere sufficienti, nel qual caso non vengono aggiunte dipendenze aggiuntive, oppure potrebbero non esserlo, nel qual caso vengono aggiunte 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 visualizzazione chiamato [Thymeleaf]. Questo artefatto include un numero molto elevato di librerie, comprese quelle per un server Tomcat incorporato. L'applicazione verrà eseguita su questo server;

Le librerie incluse in questa configurazione sono numerose:

Sopra, vediamo 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 richiesta per i progetti Spring. Per raggiungere questo obiettivo, Spring Boot esegue la configurazione automatica in base alle dipendenze presenti nel classpath del progetto. Spring Boot fornisce molte dipendenze pronte all'uso. Ad esempio, 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]. Con queste due caratteristiche:

  • dipendenze pronte all'uso;
  • configurazione automatica basata su queste dipendenze e valori predefiniti "ragionevoli", è possibile ottenere molto rapidamente un'applicazione Spring MVC funzionante. Questo è il caso del progetto qui studiato;

1.6.3. L'architettura di un'applicazione Spring MVC

Spring MVC implementa il modello architettonico MVC (Model–View–Controller):

L'elaborazione di una richiesta del client procede come segue:

  1. richiesta - gli URL richiesti hanno il formato http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... Il [Dispatcher Servlet] è la classe Spring che gestisce gli URL in entrata. Esso "instradano" l'URL all'azione che dovrebbe gestirlo. Queste azioni sono metodi di classi specifiche chiamate [Controller]. La C in MVC in questo caso è la catena [Dispatcher Servlet, Controller, Action]. Se non è stata configurata alcuna azione per gestire l'URL in entrata, il [Dispatcher Servlet] risponderà che l'URL richiesto non è stato trovato (errore 404 NOT FOUND);
  2. l'elaborazione
  • L'azione selezionata può utilizzare i parametri che il [Dispatcher Servlet] le ha passato. Questi possono provenire da diverse fonti:
    • il percorso [/param1/param2/...] dell'URL,
    • i parametri dell'URL [p1=v1&p2=v2],
    • dai parametri inviati dal browser con la sua richiesta;
  • durante l'elaborazione della richiesta dell'utente, l'azione potrebbe aver bisogno del livello [business] [2b]. Una volta elaborata la richiesta del client, questa può innescare varie risposte. Un esempio classico è:
    • una pagina di errore se la richiesta non è stata elaborata correttamente
    • una pagina di conferma in caso contrario
  • l'azione indica di visualizzare una vista specifica [3]. Questa vista mostrerà i dati noti come modello di vista. Questa è la M in MVC. L'azione creerà questo modello M [2c] e indicherà di visualizzare una vista V [3];
  1. risposta - la vista V selezionata utilizza il modello M costruito dall'azione per inizializzare le parti dinamiche della risposta HTML che deve inviare al client, quindi invia questa risposta.

Esamineremo questi diversi elementi nel progetto in esame.

1.6.4. Il controller C

  

L'applicazione importata dispone del 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, il che significa che i suoi metodi sono registrati per gestire gli URL. Un controller Spring è un singleton. Viene creata una sola istanza;
  • riga 11: l'annotazione [@RequestMapping] specifica l'URL gestito dal metodo, in questo caso l'URL [/greeting]. Vedremo in seguito che questo URL può essere parametrizzato e che è possibile recuperare questi 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 passato vuoto ed è compito dell'azione (il metodo greeting) popolarlo. Questo modello verrà passato alla vista che l'azione renderà. Si tratta quindi di un modello di vista;
  • riga 13: il valore di [name] viene inserito nel modello di vista. La classe [Model] si comporta come un dizionario;
  • riga 14: il metodo restituisce il nome della vista che dovrebbe visualizzare il modello costruito. Il nome esatto della vista dipende dalla configurazione [Thymeleaf]. In assenza di tale configurazione, la vista visualizzata qui sarà [/templates/greeting.html], dove 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à aggiunto al Classpath del progetto. Per [src/main/java], vi verranno collocate le versioni compilate del codice sorgente Java. Il contenuto della cartella [src/main/resources] viene aggiunto al Classpath senza modifiche. Possiamo quindi vedere che la cartella [templates] si troverà nel Classpath del progetto [1].

È possibile verificarlo [2-3] nella finestra [Navigator] di Eclipse [Window / Show view / Other / General / Navigator]. La cartella [target] viene creata compilando (o "costruendo") il progetto. La cartella [classes] rappresenta la radice del Classpath. È possibile notare che la cartella [templates] è presente lì.

1.6.5. La vista V

In MVC, abbiamo appena visto il controller C e il modello M. La vista V è rappresentata qui 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] imposta il contenuto del paragrafo. All'interno della stringa, abbiamo l'espressione [${name}]. Ciò significa che vogliamo il valore dell'attributo [name] dal modello di visualizzazione. Ora, ricordiamo che questo attributo è stato aggiunto al modello dall'azione:

model.addAttribute("name", name);

Il primo parametro imposta il nome dell'attributo, mentre il secondo ne imposta il 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 console. La classe [SpringApplication] alla riga 12 avvierà il server Tomcat presente nelle dipendenze e distribuirà il servizio web su di esso;
  • riga 4: possiamo vedere che la classe [SpringApplication] appartiene al progetto [Spring Boot];
  • riga 12: il primo parametro è la classe che configura il progetto, il secondo contiene eventuali parametri aggiuntivi;
  • riga 8: l'annotazione [@EnableAutoConfiguration] indica a Spring Boot di configurare il progetto;
  • riga 7: l'annotazione [@ComponentScan] fa sì che la directory contenente la classe [Application] venga scansionata alla ricerca di componenti Spring. Ne verrà trovato uno: la classe [GreetingController], che ha l'annotazione [@Controller], rendendola un componente Spring;

Eseguiamo il progetto:

 

Otteniamo 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/**] onto handler of 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 of 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)
  • riga 13: il server Tomcat si avvia sulla porta 8080 (riga 12);
  • riga 17: il servlet [DispatcherServlet] è presente;
  • riga 20: è stato individuato il metodo [hello.GreetingController.greeting], insieme all'URL che gestisce [/greeting];

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

 

Potrebbe essere interessante visualizzare le intestazioni HTTP inviate dal server. Per farlo, useremo il plugin di Chrome chiamato [Advanced Rest Client] (vedi sezione 9.6):

  • in [1], l'URL richiesto;
  • in [2], viene utilizzato il metodo GET;
  • in [3], il server ha indicato che stava inviando una risposta in formato HTML;
  • in [4], la risposta HTML;
  • in [5], richiediamo lo stesso URL ma questa volta utilizzando una richiesta POST;
  • in [7], le informazioni vengono inviate al server in formato [urlencoded];
  • in [6], il parametro name con il suo valore;
  • in [8], il browser comunica al server che sta inviando informazioni [urlencoded];
  • in [9], la risposta HTML dal 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;

Ecco come procedere:

  • in [1]: eseguiamo un goal Maven;
  • in [2]: ci sono due goal: [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]: il target viene generato;

Nota: affinché la generazione abbia esito positivo, la JVM utilizzata da STS deve essere un JDK [Finestra / Preferenze / Java / JRE installati]:

 

Nei log che compaiono nella console, è importante vedere il plugin [spring-boot-maven-plugin]. Questo è il plugin che genera l'archivio eseguibile.

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

Utilizzando una console, accedere alla 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 come segue:


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 qualsiasi servizio web che possa essere stato avviato in Eclipse (vedere pagina 17).

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

 

1.6.8. Distribuzione dell'applicazione su un server Tomcat

Sebbene Spring Boot sia molto pratico in modalità di sviluppo, un'applicazione di 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>
        <!-- thymeleaf environment -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- war generation -->
<!--         <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>

È necessario apportare modifiche in due punti:

  • riga 9: è necessario specificare che si intende generare un file WAR (Web Archive);
  • righe 24–28: è necessario aggiungere una dipendenza dall'artefatto [spring-boot-starter-tomcat]. Questo artefatto include tutte le classi Tomcat nelle dipendenze del progetto;
  • riga 27: questo artefatto è [provided], il che significa che gli archivi corrispondenti non saranno inclusi nel WAR generato. Questi archivi si troveranno invece sul server Tomcat dove verrà eseguita l'applicazione;

Infatti, se osserviamo le dipendenze attuali del progetto, vediamo che la dipendenza [spring-boot-starter-tomcat] è già presente:

  

Non è quindi necessario aggiungerlo al file [pom.xml]. Lo abbiamo commentato a titolo di riferimento.

Dobbiamo inoltre configurare l'applicazione web. In assenza di un file [web.xml], ciò avviene utilizzando una classe che estende [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 sovrascritto (riga 8);
  • riga 10: viene fornita la classe che configura il progetto;

Per eseguire il progetto, procedere come segue:

  • in [1], eseguire il progetto su uno dei server registrati nell'IDE Eclipse;
  • in [2], selezionare [Tomcat v8.0] in alto;

Una volta fatto ciò, è possibile inserire l'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 operazione potrebbe non andare a buon fine. È quanto è accaduto, ad esempio, con [Apache Tomcat 8.0.3 e 8.0.15]. Nell'esempio sopra riportato, la versione di Tomcat utilizzata era la [8.0.9].

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

1.7. Un secondo progetto Spring MVC

1.7.1. Il progetto demo

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

I servizi web accessibili tramite URL standard che restituiscono dati JSON sono spesso chiamati servizi REST (REpresentational State Transfer). In questo documento, mi riferirò semplicemente al servizio che stiamo per realizzare come servizio web/JSON. Un servizio è detto RESTful se segue determinate regole. Non ho cercato di attenermi a queste regole.

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 specifichi il tipo di file prodotto dalla build di Maven. In sua assenza, viene utilizzato il tipo [jar]. L'applicazione è quindi un'applicazione eseguibile basata su console, 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 definisce la maggior parte delle dipendenze del progetto. Esse potrebbero essere sufficienti, nel qual caso non vengono aggiunte dipendenze aggiuntive, oppure potrebbero non esserlo, nel qual caso vengono aggiunte le dipendenze mancanti;
  • righe 17–20: L'artefatto [spring-boot-starter-web] include le librerie necessarie per un progetto di servizio web Spring MVC in cui non vengono generate viste. Questo artefatto include un numero molto elevato di librerie, comprese quelle per un server Tomcat incorporato. L'applicazione verrà eseguita su questo server;

Le librerie incluse in questa configurazione sono numerose:

Sopra, vediamo i tre archivi del server Tomcat.

1.7.3. L'architettura di un servizio Spring [web / JSON]

Vediamo come Spring MVC implementa il modello MVC:

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

  1. richiesta - gli URL richiesti hanno il formato http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... Il [Dispatcher Servlet] è la classe Spring che gestisce gli URL in entrata. Esso "instradano" l'URL verso l'azione che deve elaborarlo. Queste azioni sono metodi di classi specifiche chiamate [Controller]. La C in MVC in questo caso è la catena [Dispatcher Servlet, Controller, Action]. Se non è stata configurata alcuna azione per gestire l'URL in entrata, il [Dispatcher Servlet] risponderà che l'URL richiesto non è stato trovato (errore 404 NOT FOUND);
  2. l'elaborazione
  • L'azione selezionata può utilizzare i parametri che il [Dispatcher Servlet] le ha passato. Questi possono provenire da diverse fonti:
    • il percorso [/param1/param2/...] dell'URL,
    • i parametri dell'URL [p1=v1&p2=v2],
    • dai parametri inviati dal browser con la sua richiesta;
  • durante l'elaborazione della richiesta dell'utente, l'azione potrebbe aver bisogno del livello [business] [2b]. Una volta elaborata la richiesta del client, questa può innescare varie risposte. Un esempio classico è:
    • una pagina di errore se la richiesta non è stata elaborata correttamente
    • una pagina di conferma in caso contrario
  • l'azione istruisce una vista specifica a renderizzare [3]. Questa vista visualizzerà i dati noti come modello di vista. Questa è la M in MVC. L'azione creerà questo modello M [2c] e istruirà una vista V a renderizzare [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 questa risposta.

Per un servizio web / JSON, l'architettura precedente viene leggermente modificata:

  • in [4a], il modello, che è una classe Java, viene convertito 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 dispone del 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] rende la classe [GreetingController] un controller Spring, il che significa che i suoi metodi sono registrati per gestire gli URL. Abbiamo già visto l'annotazione simile [@Controller]. Il tipo di ritorno dei metodi di quel controller era [String], ovvero il nome della vista da visualizzare. Qui è diverso. I metodi di un [@RestController] restituiscono oggetti che vengono serializzati per essere inviati al browser. Il tipo di serializzazione eseguita dipende dalla configurazione di Spring MVC. Qui, saranno serializzati in JSON. È la presenza di una libreria JSON nelle dipendenze del progetto che fa sì che Spring Boot configuri automaticamente il progetto in questo modo;
  • riga 14: l'annotazione [@RequestMapping] specifica l'URL gestito dal 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 l'accesso concorrente. Più thread potrebbero voler incrementare la variabile [counter] contemporaneamente. Ciò verrà gestito correttamente. Un thread può leggere il valore del contatore solo una volta che il thread che lo sta modificando ha terminato la modifica.

1.7.5. Il modello M

Il modello M prodotto 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;
    }
}

Convertendo questo oggetto in JSON si otterrà la stringa {"id":n,"content":"text"}. Alla fine, la stringa JSON generata dal metodo del controller avrà il seguente formato:

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

oppure

{"id":2,"content":"Ciao, 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à incontrato e spiegato questo codice nell'esempio precedente.

1.7.7. Esecuzione del progetto

Eseguiamo il progetto:

 

Otteniamo 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/**] onto handler of 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 [/**] onto handler of 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)
  • riga 13: il server Tomcat si avvia sulla porta 8080 (riga 12);
  • riga 17: il servlet [DispatcherServlet] è presente;
  • riga 20: il metodo [GreetingController.greeting] è stato individuato;

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

 

Riceviamo la stringa JSON prevista.

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

Potrebbe essere utile visualizzare le intestazioni HTTP inviate dal server. Per farlo, useremo il plugin di Chrome chiamato [Advanced Rest Client] (vedi Appendici, sezione 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 in formato JSON;
  • in [5], richiediamo lo stesso URL ma questa volta utilizzando una richiesta POST;
  • in [7], le informazioni vengono inviate al server in formato [urlencoded];
  • in [6], il parametro name con il suo valore;
  • in [8], il browser comunica al server che sta inviando dati [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]: eseguiamo un target Maven;
  • in [2]: ci sono due goal: [clean] per eliminare la cartella [target] dal progetto Maven, [package] per rigenerarla;
  • in [3]: la cartella [target] generata si troverà in questa cartella;
  • in [4]: generiamo il target;

Nei log che compaiono nella console, è importante vedere il plugin [spring-boot-maven-plugin]. Questo è il plugin che genera l'archivio eseguibile.

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

Utilizzando una console, accedere alla 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 come segue:


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 prima arrestare qualsiasi servizio web che possa essere stato avviato in Eclipse (vedere la sezione 1.6.6).

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

 

1.7.9. Distribuzione dell'applicazione su un server Tomcat

Come abbiamo fatto per il progetto precedente, modifichiamo 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-rest-service</artifactId>
    <version>0.1.0</version>
    <packaging>war</packaging>
 
    ...
</project>
  • Riga 9: È necessario specificare che si intende generare un file WAR (Web Archive);

È inoltre necessario configurare l'applicazione web. Se non è presente un file [web.xml], ciò viene effettuato utilizzando una classe che estende [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 sovrascritto (riga 8);
  • riga 10: viene fornita la classe che configura il progetto;

Per eseguire il progetto, procedere come segue:

  • In [1-2], eseguire il progetto su uno dei server registrati nell'IDE Eclipse;

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

 

1.8. Conclusione

Abbiamo presentato 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, sono necessarie due dipendenze Maven per il progetto:


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

Queste configurazioni introducono un gran numero di dipendenze a cascata, molte delle quali non sono necessarie. Per distribuire l'applicazione, useremo una configurazione Maven manuale che include solo le dipendenze necessarie per il progetto.

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

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