Primi Passi con Spring MVC (parte 2)

In questa seconda parte della serie di articoli dedicati a Spring MVC vediamo come “convertire” la semplice applicazione servlet per il login utente presentata nell’articolo Primi Passi con Spring MVC (parte 1), in una applicazione Spring MVC.

Innanzitutto accenniamo brevemente al significato dell’acronimo MVC, che è l’abbreviazione per Model-View-Controller. Con tale termine si indica un pattern architetturale, principalmente utilizzato nella programmazione orientata agli oggetti, per indicare una separazione netta e distinta tra: il modello dei dati, la vista che li visualizza, ed il controllore che li manipola. Ovviamente tali concetti li ritroveremo in Spring MVC.

Per l’utilizzo di Spring MVC dobbiamo innanzitutto inserire nel pom.xml la relativa dipendenza che naturalmente si aggiunge a javaee-web-api che implementa le Servlet API:

Relativamente alle viste, invece, le pagine jsp del progetto non subiscono alcuna modifica.

DispatcherServlet

Molti framework java orientati al web si fondano sulle Servlet API ed implementano una servlet dedicata come unico punto di accesso alle funzionalità del framework. In JSF, ad esempio, è presente la FacesServlet, mentre in Struts abbiamo la ActionServlet.Spring MVC utilizza invece la DispatcherServlet, che implementa molti degli algoritmi comuni mentre demanda operazioni specifiche a componenti delegate configurabili. La servlet può essere configurata utilizzando JavaConfig oppure, più tradizionalmente, dichiarandola nel descrittore web.xml:

La servlet è configurata utilizzando un contesto di Spring orientato al web e che è definito dalla interfaccia WebApplicationContext che estende ApplicationContext. Il parametro contextConfigLocation indica alla Servlet dove si trova l’XML per la configurazione del contesto di Spring e che, nel nostro caso, viene interpretato mediante la classe XmlWebApplicationContext. Il file app-config.xml che definisce il contesto di spring è il seguente:

Nel file XML sono presenti due soli tag, la cui funzione è:

  • <context:component-scan>: consente di eliminare la necessità di configurare tutti i bean nell’XML indicando a Spring (core) i package dal quale iniziare lo scan alla ricerca di classi annotate con @Component, @Repository, @Service e @Controller. Utilizzando questo tag non è necessario inserire nell’XML anche il tag <context:annotation-config> che invece indica a Spring di riconoscere le annotazioni @Autowire, @Required e @Qualifier per l’iniezione delle componenti.
  • <mvc:annotation-driven/>: in una applicazione MVC indica a Spring di riconoscere i relativi componenti, cosa che già avverrebbe grazie alla presenza del tag context:component-scan>. Inoltre forza la registrazione nel contesto di Spring di alcuni bean (di default) che altrimenti dovrebbero essere inseriti esplicitamente nell’XML, e l’attivazione di alcune configurazioni. Per un approfondimento rimando a questo ottimo post spring-mvc-component-scan-annotation-config-annotation-driven.

Controller

Il ruolo svolto dalla servlet nel progetto originale è ora svolto da un oggetto controller. Diversamente dal caso della servlet, in cui è necessario estendere la classe javax.servlet.http.HttpServlet, in Spring MVC il controller non deve estendere o implementare alcuna interfaccia, ma la sola operazione richiesta è di annotare il bean con l’annotazione @Controller.

Per effetto della direttiva component-scan tutte le classi annotate in tale modo vengono riconosciute come controller per le richieste HTTP provenienti dal front-end. L’associazione del bean alla url gestita ed al metodo HTTP avviene invece per mezzo di un’altra annotation @RequestMapping che può essere associata sia alla classe che ai metodi della classe per una più fine configurazione. Per replicare quanto fatto nella servlet originale abbiamo bisogno di un metodo che mostri la pagina di login e di uno che gestisca la sottomissione della form. Il controller diviene quindi:

Come è evidente dal codice il metodo showLoginPage() è invocato dalla DispatcherServlet quando dal browser arriva una richiesta GET sull’url /login, mentre il metodo handleUserLogin() è invocato quando arriva una richiesta POST sullo stesso url.

Model

Per il recupero dei parametri associati alla request il controller non ha bisogno di accedere all’oggetto HttpServletRequest, come nel caso delle servlet, ma Spring implementa un meccanismo dichiarativo in cui, attraverso annotation, indica quali parametri devono essere passati al controller. Il metodo handleUserLogin(), ad esempi, dichiara i parametri name e password annotandoli con @RequestParam. Questo indica alla DispatcherServlet che nella request HTTP saranno presenti due parametri, rispettivamente name e password, che dovrà recuperare e passare al metodo.

In modo analogo esistono altre annotation con cui possono essere annotati i parametri di un metodo del controller al fine di accedere a diversi elementi della request, come ad esempio @RequestHeader, @CookieValue,@RequestBody, etc., ma non saranno oggetto del presente articolo.

Un altro parametro che svolge un ruolo significativo ne metodo handleUserLogin() è model di tipo org.springframework.ui.ModelMap. Si tratta dell’oggetto che sarà passato alla vista per il rendering dei valori passati in response, ovvero quelli che nel caso della servlet erano inseriti nell’oggetto HttpServletResponse.

ViewResolver

Se proviamo ad eseguire l’applicazione così com’é ed inseriamo nel browser l’url http://localhost:8080/login otterremo il seguente errore:

HTTP Status 500 - Circular view path [login]: would dispatch back to the current handler URL [/login] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)

Questo accade perchè i metodi del controller restituiscono i nomi delle viste su cui redirigere il browser e nel caso di  showLoginPage() il nome restituito è login che è esattamente l’url definito nell’annotazione @RequestMapping del metodo stesso. Questo provoca appunto un Circular view path.

Quello che manca è di istruire il DispatcherServlet in modo che il nome della vista login sia associato la pagina login.jsp definita nel percorso /WEB-INF/views. Questa mapping è ottenuto introducendo il componente ViewResolver.

Spring MCV mette a disposizione diversi ViewResolver in funzione di come si realizzi il mapping richiesto. Nel nostro caso abbiamo bisogno di configurare un InternalResourceViewResolver in quanto le jsp sono inserite all’interno della cartella WEB-INF e quindi sono, per definizione, internal resource view e quindi accessibili esclusivamente tramite servlet (o controller).

Per configurare  il ViewResolver è sufficiente definire il relativo bean nel file app-config.xml come segue:

L’effetto è quello di modificare il nome delle viste restituite dai controller inserendo il prefisso WEB-INF/views/ ed un suffisso .jsp. Conseguentemente le viste restituite dai metodi del nostro controller saranno mappate in:

login WEB-INF/views/login.jsp
welcome WEB-INF/views/welcome.jsp

Il Processo

Ricapitoliamo il processo che è utilizzato da Spring MVC per soddisfare una richiesta HTTP proveniente all’applicazione. Per farlo ci avvaliamo dell’immagine seguente, rubata dal sito onlinetutorialspoint.

Consideriamo il caso, più completo, della sottomissione della maschera di login:

  1. Il browser esegue una request di tipo POST all’url /login che è intercettata dal Front Controller ovvero dalla DispatcherServlet.
  2. La servlet decide a quale controller delegare la gestione della richiesta in base all’header della request ed, ovviamente, alla configurazione del contesto MVC.
  3. Il metodo handleUserLogin() della classe LoginController prende in carico la richiesta e la elabora.
  4. Terminata l’elaborazione il controller popola il model, in funzione dell’esito.
  5. Il controller restituisce il model al Front Controller.
  6. La servlet delega la costruzione della vista ad un view template identificato in base al utilizza il ViewResolver.
  7. La vista è generata in base al model ed alla jsp ed è restituita la Front Controller.
  8. La DispatcherServlet invia la pagina generata al browser.

Codice Sorgente

Il codice sorgente completo e funzionante dell’applicazione è scaricabile qui login-sprmvc.

© 2019 Java Boss - Theme by HappyThemes