Vediamo in questo articolo l‘implementazione in Spring del pattern Model View Controller per le applicazioni web.Spring offre infatti una infrastruttura per il Model View Controller orientato alle applicazioni web che è in grado di sostituire soluzioni più navigate come Struts. Offre tra l‘altro una varietà di scelta nel set di componenti utilizzabili che consente una notevole flessibilità di progettazione.
Introduzione
Le applicazioni web devono superare tramite una progettazione accurata le lacune derivanti dall‘utilizzare un protocollo “povero” come l‘HTTP. Il pattern Model View Controller è largamente utilizzato perché permette di separare in strati ben definiti le problematiche di tali applicazioni.
Spring offre un pacchetto dedicato al Web MVC che permette di soppiantare senza troppi rimorsi altre soluzioni open source come Struts. Il pacchetto di Spring presenta diverse possibilità ritagliate sulle casistiche più comuni, al contrario di Struts che ha una struttura molto rigida. Inoltre la soluzione Spring come al solito sfrutta le caratteristiche dell‘Inversion of Control per massimizzare il disaccopiamento dei componenti. In ogni caso, se fosse necessario, l‘integrazione con altri sistemi di controllo come Struts non presenta particolari difficoltà .
Un piccolo ripasso sull‘MVC
Il pattern MVC definisce la composizione in strati ben definiti di un sistema software. Il “Model” rappresenta quello strato che permette di accedere e modificare lo stato del sistema. La “View” gestisce invece la rappresentazione sotto forma di interfaccia utente dei dati del Model e permette di interagire con esso attraverso lo strato “Control”. Quest‘ultimo rappresenta il collante dei primi due strati e li coordina secondo delle regole prefissate in fase di progettazione. L‘implementazione dell‘MVC nel mondo Web prevede normalmente per lo strato Control l‘utilizzo di un altro pattern denominato “Front Controller”. Tale pattern prevede un unico ingresso per le chiamate HTTP rappresentato da una servlet che svolge il ruolo per l‘appunto di Front Controller. Tale servlet ha il compito di delegare l‘elaborazione a un qualche componente che possiamo chiamare “controller”.
Il controller normalmente, a sua volta, delegherà l‘esecuzione a componenti applicativi specializzati. Il risultato dell‘elaborazione del “controller” sarà un qualche componente che rappresenti una porzione dello stato corrente del sistema. Il Front Controller “raccoglie” questo componente e lo fornisce allo strato View che si occupa di visualizzarlo nella maniera prevista dall‘interfaccia utente. Per chiunque conosca Struts questi concetti dovrebbero essere abbastanza familiari.
Generalità sull‘implementazione dell‘MVC di Spring
Il Front Controller di Spring è la classe DispatcherServlet. La DispatcherServlet ha il compito di coordinare i componenti che costituiscono gli strati applicativi secondo l‘implementazione Web MVC di Spring. La DispatcherServlet inoltra la singola richiesta http a un componente Controller utilizzando un HandlerMapping. L‘HandlerMapping è un componente specializzato nell‘interpretare le richieste e decidere a quale Controller esse vanno inoltrate. Il Controller a sua volta esegue la sua logica applicativa, normalmente delegandola a oggetti specifici, e alla fine restituisce un oggetto ModelAndView. L‘oggetto ModelAndView serve a contenere un oggetto View o semplicemente il nome di un oggetto View e l‘oggetto o la lista di oggetti che rappresentano una porzione dello stato corrente del sistema trattato dal controller (il model). La DispatcherServlet “raccoglie” l‘oggetto ModelAndView restituito dal controller e se contiene un riferimento a un oggetto view inoltra la richiesta a tale oggetto fornendogli il model, se contiene solamente il nome di una view utilizza un oggetto ViewResolver specializzato nell‘interpretare il nome fornito nel ModelAndView e fornire l‘oggetto view incaricato di elaborare il risultato da restituire al client.
Configurare la DispatcherServlet
La DispatcherServlet si configura come una qualunque servlet, come mostrato di seguito:
frontcontroller
org.springframework.web.servlet.DispatcherServlet
1
...
frontcontroller
*.htm
La sezione “servlet-mapping” associa un “pattern” arbitrario alle richieste http che devono essere gestite dalla DispatcherServlet. Nell‘esempio, tutte le richieste con URL che finiscono con .htm verrano gestite dalla DispatcherServlet. Quest‘ultima per default cerca di caricare l‘ApplicationContext di Spring da un file denominato
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
/WEB-INF/app-service.xml,/WEB-INF/app-data.xml
Il ContextLoaderListener configurato in questo esempio utilizza il parametro contextConfigLocation per caricare i due file di configurazione app-service.xml e app-data.xml nell‘ApplicationContext. In questo modo è possibile separare la configurazione dei diversi strati applicativi, in questo caso lo strato di servizio e quello di accesso ai dati.
I Controller
Il ruolo di controller è interpretato dall‘interfaccia Controller. Tale interfaccia prevede un metodo
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
...
}
che come accennato precedentemente restituisce al front controller un oggetto di tipo ModelAndView. Il ruolo del controller è praticamente quello che in Struts spetta alle Actions. A differenza di Struts, tuttavia, esiste una gerarchia variegata di implementazioni che copre i casi di utilizzo più comuni.
La classe AbstractController è all‘apice della gerarchia ed estendendola direttamente è possibile implementare dei semplici controller che non gestiscono dei dati in ingresso ma restituiscono solamente dati del modello da visualizzare a video. A questo scopo è necessario implementare il metodo seguente dell‘AbstractController:
public ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response)
throws Exception {
...
}
Nel caso invece che la logica applicativa debba gestire dei parametri in ingresso è possibile estendere la classe AbstractCommandController che permette di popolare automaticamente dei beans con i parametri provenienti dalla request e di configurare dei componenti specializzati nel validare tali parametri, ossia verificarne la correttezza. Il metodo principale della AbstractCommandController è:
protected ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object command,
BindException errors) throws Exception {
...
}
Occorre notare che tale metodo oltre alla request e alla response prende in ingresso il parametro command che è un oggetto generico. Il parametro command è quello che si definisce un “command object” ossia un bean che serve a contenere i parametri della richiesta e che è valorizzato automaticamente dal controller.
Per gestire invece la situazione leggermente più complessa nella quale il controller deve gestire sia la visualizzazione a video di un form utente, ossia una richiesta di tipo GET, sia l‘elaborazione a fronte di un richiesta di tipo POST del form stesso occorre estendere la classe SimpleFormController:
public class MioController
extends SimpleFormController {
public MioController() {
setCommandClass(MiaClasse.class);
}
protected void doSubmitAction(Object command)
throws Exception {
...
}
}
mioForm
success
Il doSubmitAction è un pò diverso dai metodi usati precedemente. Il SimpleFormController permette di spostare la gestione della view da visualizzare in fase di configurazione piuttosto che codificata nel metodo. Il metodo doSubmitAction prende in ingresso solamente un parametro di tipo Object che rappresenta il command object. Nella configurazione, poi, si definisce il nome della view che rappresenta il form e di quella che deve essere visualizzata in caso di successo nell‘elaborazione.
Occorre notare però che il metodo doSubmitAction non permette di restituire dati dello strato model. Se si vuole restituire dei dati a video a fronte dell‘elaborazione del form occorre sovrascrivere il metodo onSubmit al posto del doSubmitAction:
protected ModelAndView onSubmit(Object command,
BindException errors) throws Exception {
...
return new ModelAndView("nomeView","modelData", modelData);
}
È possibile associare al controller un oggetto specializzato nel controllo della validità dei campi. Tale oggetto deve implementare l‘interfaccia Validator e può essere “iniettato” nel controller con la proprietà omonima:
...
Il flusso operativo, nel caso il validatore restituisca un responso negativo, prevede che venga visualizzato nuovamente il form definito in configurazione tramite la proprietà formView.
Gli Handler Mappings
L‘interfaccia org.springframework.web.servlet.HandlerMapping è l‘astrazione di Spring che si occupa di mappare gli URL di chiamata ai controllers. Le implementazioni principali a disposizione sono la classe BeanNameUrlHandlerMapping e SimpleUrlHandlerMapping. La BeanNameUrlHandlerMapping fa una cosa molto diretta e semplice: interpreta la porzione di URL di chiamata come nome del controller da eseguire. Nell‘esempio seguente è mostrata la configurazione di un‘handler mapping di questo tipo e di un controller:
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> class="spring.esempi.mvc.MioController">
Il beanNameUrlMapping configurato sopra associa una chiamata contenente la porzione di URL /home.htm al bean omonimo che rappresenta un‘istanza della classe MioController.
La SimpleUrlHandlerMapping è invece un pò più “intelligente”:
"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
mioController1
mioController2
È possibile qui associare esplicitamente la porzione di URL al nome del controller. Sarà quindi possibile configurare per esempio il controller relativo all‘URL /home.htm in questo modo:
class="spring.esempi.mvc.MioController1">
Piccolo esempio
L‘esempio di seguito riportato mostra lo scenario più semplice di un componente che implementa direttamente l‘interfaccia Controller. Si vuole caricare la home page di un sito, pagina che nel nostro caso contiene solamente un messaggio di benvenuto. Occorre scrivere prima di tutto la classe controller:
public class HomeController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
return new ModelAndView("home", "messaggio", messaggio);
}
private String messaggio;
public void setMessaggio(String messaggio) {
this.messaggio = messaggio;
}
}
E poi configurarla nell‘ApplicationContext di Spring secondo le regole consuete:
class="spring.esempi.mvc.MioController">
Benvenuti nella home page!
Per default, la DispatcherServlet utilizza l‘handler mapping BeanNameUrlHandlerMapping che interpreta direttamente la porzione di URL (/home.htm nel nostro caso) che richiama la DispatcherServlet come nome del controller da eseguire. Infine è necessario definire un View Resolver in grado di interpretare il nome della view fornita nel ModelAndView (“home” nell‘esempio) e associarla a un oggetto view specifico. Qui viene usata la classe InternalResourceViewResolver che utilizza il nome fornito dal ModelAndView e costruisce il percorso relativo al contesto della web application di una pagina Jsp:
/WEB-INF/jsp/
.jsp
Il viewResolver restituisce alla DispatcherServlet il percorso /WEB-INF/jsp/home.jsp che la DispatcherServlet fornisce a un oggetto view, il quale si occupa di inoltrare la request corrente alla jsp data. Omettiamo l‘implementazione della pagina JSP, che è banale: non dovrà far altro che mostrare a video la variabile “messaggio” fornita dal ModelAndView.
Conclusioni
Il pacchetto di Spring dedicato al MVC Web va a completare un elenco di funzionalità che già copre tutte le problematiche principali delle applicazioni Java. Ci sono comunque svariati aspetti che nella trattazione di questa serie di articoli non sono stati toccati, e questo fa capire la ricchezza e la completezza di questo framework. Nel prossimo e ultimo articolo di questa serie verrà trattata una variante dell‘MVC orientata ai sistemi basati su portlets.
Riferimenti
[1] “Spring Reference” 2.0
http://www.springframework.org/
[2] Craig Walls – Ryan Breidenbach,”Spring in Action”, Manning, 2005