16. Introduzione a Spring MVC
16.1. 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. L’utente 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 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. Una specifica denominata JPA (Java Persistence API) consente di astrarsi dal ORM utilizzato, qualora quest’ultimo implementi tali specifiche. Questo sarà il caso in questo tutorial e d'ora in poi chiameremo il livello ORM, il livello JPA;
- l’integrazione dei livelli è gestita dal framework Spring;
16.2. 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:
- 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.
- Elaborazione
- (continua)
- 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/...] del 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];
- 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;
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 correlati oppure no. Prendiamo un’applicazione web Spring MVC a un 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 MVC e si ottiene 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 del 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.
16.3. Un progetto web / jSON con Spring MVC
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.
16.3.1. Il progetto dimostrativo
![]() |
- in [1], importiamo una delle guide Spring;
![]() |
- in [2], selezioniamo 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). Un servizio è detto Restful se rispetta determinate regole.
Esaminiamo ora il progetto importato, iniziando dalla sua configurazione Maven.
16.3.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.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<start-class>hello.Application</start-class>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
- righe 6-8: le proprietà del progetto Maven. Manca un tag [<packaging>] che indichi il tipo di file generato 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-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.
16.3.3. L’architettura di un servizio Spring [web / jSON]
Per un servizio web / jSON, Spring MVC implementa il modello MVC nel modo seguente:
![]() |
- 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;
16.3.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] rende la classe [GreetingController] un controller Spring, ovvero i suoi metodi sono registrati per gestire i 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 contemporaneamente la variabile [counter]. L’operazione avverrà in modo corretto. Un thread può leggere il valore del contatore solo se il thread che lo sta modificando ha completato la modifica.
16.3.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:
oppure
16.3.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. Eseguiamo il progetto:
![]() |
Si ottengono i seguenti log della console:
- 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 URL [http://localhost:8080/greeting]:
![]() | ![]() |
Si riceve correttamente la stringa jSON prevista. Potrebbe essere interessante dare un’occhiata alle intestazioni HTTP inviate dal server. A tal fine, utilizzeremo l'estensione di Chrome denominata [Advanced Rest Client] (Chrome / Ctrl-T / Menu [Applications] / [Advanced Rest Client] - cfr. Allegati, paragrafo 23.11):
![]() |
- 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 una 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;
16.3.7. Creazione di un archivio eseguibile
Ora 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 (cfr. [pom.xml] di seguito):
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Con 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)
...
Ora che l'applicazione web è avviata, è possibile accedervi tramite un browser:
![]() |
16.3.8. 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 il URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] in un browser:
![]() |
16.4. Conclusion
Abbiamo introdotto un tipo di progetto Spring MVC in cui l’applicazione web invia un flusso jSON al browser. Ora svilupperemo un'applicazione web / jSON per rendere accessibile sul web il database [dbproduitscategories] studiato nei capitoli precedenti.























