|
a risolvere tutti i problemi, ma mai nessuna di esse potra' porne uno. Einstein
|
|
|
|
|
seconda parte - Spring MVC, Inversione del Controllo e Dependency Injection
By
Massimo Cimino
11 giugno 2007
|
 |
|
In questa seconda parte ci proponiamo di dare uno sguardo alla implementazione fornita da Spring del paradigma MVC.
Vedremo il funzionamento della gestione delle incoming request e che ruolo gioca la Dispatcher Servlet. Infine presenteremo un piccolo esempio che realizza un caso d’uso classico di visualizzazione su una pagina jsp una insieme di contenuti ricavati da uno storage qualsiasi.
E’ possibile scaricare il codice di esempio usato nell’ articolo: ExampleSpring.war
Nota bene: Per far funzionare la web application di esempio, va aggiunta la libreria spring.jar nella cartella WEB-INF/lib
|
|
|
DispatcherServlet
|
top
|
Il framework Spring MVC è progettato intorno ad una Servlet la DispatcherServlet il suo compito è quello di rispondere alle richieste in arrivo delegando a degli Handler.
Il diagramma seguente illustra il flusso del framework per rispondere ad una richiesta proveninete da un client. La DispatcherServlet agisce come “Front Conrtoller” e delega ai Controller la gestione delle richieste in arrivo. Il compito del Controller è quello di processare la richiesta, eseguendo la logica di back end necessaria e alla fine generare un oggetto ModelAndView che la DispatcherServlet utilizzerà per effettuare il rendering della response con la collaborazione di un ViewTemplate.

La dichiarazione della DispatcherServlet come per tutte le servlet deve essere fatta all’interno del web.xml della web application :
<web-app> <servlet> <servlet-name>disp</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>disp</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping> </web-app> Nella configurazione riportata in questo esempio tutte le richieste che terminano con .htm saranno gestite dalla disp DispatcherServlet.
La DispatcherServlet inoltre è completamente integrata con l’ IoC container di Spring e durante la sua inizializzazione, il framework andrà a cercare un file denominato disp-servlet.xml all’interno della directory WEB-INF della web application. Questo file contiene il WebApplicationContext della applicazione, all’interno del quale saranno configurati tutti gli oggetti che hanno a che fare con lo strato web come ad esempio fi controller, gli Handler Mapping e i vari Resolver (ViewResolver, LocaleResolver ed altri ) utilizzati dalla servlet.
|
|
CONTROLLERS
|
top
|
Spring MVC delega la responsabilità di gestire le richieste in arrivo a degli oggetti denominati Controllers. I controllers, così come le servlet sono mappati ad una o più URL e hanno accesso all'HttpServletRequest e HttpServletResponse . La loro responsabilità è quella di interagire con lo strato sottostante della logica di business per processare le richieste, creare gli oggetti che saranno necessari alla visalizzazione della response (il model) ed infine hanno la responsabilià del flusso, ovvero dovranno decidere quale sarà la view che il client vedrà a seguito della sua request.
Spring MVC mette a disposizione una quantità di implementazioni di Controllers per gestire varie situazioni tra le quali :
• Richieste senza work flow • Form Controllers per la gestione completa del ciclo di vita di un form • wizard controllers per i flussi di form distribuiti su più pagine • controllers multi action per aggregare più action in un solo controller.
Vale la pena notare che i Controller – come si può vedere lell'esempio sottostante - sono in Spring disaccoppiati dalla particolare tecnologia di rendering utilizzata. Infatti per i Controllers il model è una Map di oggetti qualsiasi mentre la View non è altro che un nome logico che sarà successivamente risolto da un oggetto che implementa l'interfaccia ViewResolver.
public class SampleController extends AbstractController { public ModelAndView handleRequestInternal( HttpServletRequest request, HttpServletResponse response) throws Exception { //processing the request ... //selecting the view ModelAndView mav = new ModelAndView("hello"); //adding the model to the view ... mav.addObject("message", "Hello World!"); return mav; } }
|
|
Handler mappings
|
top
|
Abbiamo visto come i Controller siano gli oggetti a cui la DispatcherServlet delega la gestione delle incoming request. Ci si potrebbe chiedere con che meccanismo la servlet è in grado di capire qual'è il controller giusto da usare per una certa richiesta in arrivo . In altri termini qual'è il meccanismo con il quale le richieste dei client vengono mappate ai relativi Controller. Questo è esattamente il compito degli HandlerMapping. Un HandlerMapping è un bean speciale configurato nel WebApplicationContext della DispatcherServlet con un id particolare : ”handlerMapping” (un nome di fantasia ... )
Spring come al solito mette a disposizione una certa varietà di HandlerMapping. Uno molto semplice ma allo stesso tempo molto potente è BeanNameUrlHandlerMapping .
<beans> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean name="/editaccount.form" class="org.springframework.web.servlet.mvc.SimpleFormController">
<property name="formView" value="account"/> <property name="successView" value="account-created"/> <property name="commandName" value="account"/> <property name="commandClass" value="samples.Account"/> </bean> <beans>
Con BeanNameUrlHandlerMapping si vogliono mappare i controller con le richieste in funzione del nome del controller.
Nell'esempio riportato sopra una richiesta http con URL http://localhost/editaccount.form sarà gestita dal controller il cui attributo name dovrà essere /editaccount.form
Un altro HandlerMapping che si può utilizzare il SimpleUrlHandlerMapping. Con questo tipo di mapping si può associare esplicitamente la URL della request con il relativo controller direttamente all'interno del WebApplicationContext con una sintassi simile a quella di ANT. Nell'esempio riportato immediatamente sotto le richieste per help.html in qualunque directory verranno gestite da helpController così come le richieste che cominciano per view e finiscono per .html nella directory /ex
<bean id ="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <value> /ex/view*.html=helpController /**/help.html=helpController </value> </property> </bean>
<bean id="helpController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
All'interno delle defnizioni degli handlerMapping è anche possibile definire degli interceptors. Gli interceptors sono classi che implementano l'interfaccia HandlerInterceptor,che ha i seguenti tre metodi che vengono eseguiti boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
La dispatcher servlet esegue il metodo preHandle di tutti gli interceptors registrati all’interno del WebApplicationContext. Il valore booleano ritornato se è pari a false false determina l’interruzione della catena di esecuzione. Immediatamente dopo viene invocato il metodo handle sul Controller mappato per richiesta. Di seguito viene chiamato il metodo postHandle sulla lista degli interceptors . Il metodo afterCompletion viene invocato sugli interceptors successivamente alla fase di rendering della View.
Nell’esempio sotto riportato viene registrato un Interceptor per tutte le richieste in entrata
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="myInterceptor"/> </list> </property> <property name="mappings"> <value> /ex/view*.html=helpController /**/help.html=helpController </value> </property> </bean>
<bean id="myInterceptor" class="samples.MyInterceptorOne"/>
<bean id="helpController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
|
|
Views
|
top
|
Come abbiamo visto poco fa tutti i Controllers, indipendentemente dalla loro natura, ritornano alla DispatcherServlet una istanza di un ModelAndView il cui costruttore accetta in ingresso una stringa che rappresenta il nome logico dellaa View .
Spring mette a disposizione un certo numero di ViewResolver che sono gli oggetti che risolvono il nome logico della View e lo associano ad una risorsa da renderizzare. Tali oggetti naturalmente vanno configurati nel WebApplicationContext della dispatcherServlet :
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean>
Alcuni tra i più comuni resolver sono :
- XmlViewResolver : Usa un file di configurazione xml per la risoluzione delle view. La property “location” definisce il path del file di configurazione relativamente alla root della web application.
- ResourceBundleViewResolver : Usa un resource bundle (una serie di file con estensione .properties ) per risolvere le view.
- UrlBasedViewResolver : Esegue una risoluzione diretta del nome simbolico della view in una URL.
Inoltre Spring fornisce supporto per molte diverse tecnologie di rendering, tra cui : - JSP e JSTL - Velocity - FreeMarker
|
|
The sample application
|
top
|
Bene dopo tanto chiaccherare è arrivato il momento di sporcarsi le mani e scrivere una web application che faccia uso di Spring MVC. Quello che cercheremo di realizzare è un semplice caso d’uso che visualizzerà in una pagina jsp un ipotetico elenco di dipendenti di una azienda o di un dipartimento, acquisendo i dati cioè la lista dei dipendenti, da un database.
Per creare la applicazione web useremo Eclipse e Tomcat 5.5 sarà il nostro servlet engine.
|
|
Configurazione della web application
|
top
|
Per prima cosa, dopo aver creato un Dinamic Web Project in Eclipse che chiameremo ExampleSpring e dopo aver posizionato il file spring.jar dentro la Directory WEB-INF/lib, dovremo metter mano al ewb.xml per configurare la DispatcherServlet.
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name> ExampleSpring</display-name> <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.form</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app>
Per la DispatcherServlet dobbiamo anche scrivere il relativo WebApplicationContext che nel caso in questione, poiché il servlet-name scelto è dispatcher dovrà essere denominato dispatcher-servlet.xml: all’interno di questo file andiamo a definire un BeanNameUrlHandlerMapping, un UrlBasedViewResolver e un controller mappato sulla URL : http://localhost:8080/ExampleSpring/listEmployee.htm.
In ultimo viene definito il bean che rappresenta l'accesso allo strato dei servizi : serviceBean che viene iniettato nel controller con il metodo “setter based”. Il controller pertanto dovrà avere un attributo di tipo EmployeeInfoControl con relativo metodo “setter”.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" /> <bean name="/listEmployee.htm" class="controllers.ListEmployeeController"> <property name="service" ref="serviceBean" /> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <bean id="serviceBean" class="services.EmployeeInfoControlFakeImpl"> </bean> </beans>
|
|
IL CONTROLLER
|
top
|
Così come è scritto nel WebApplicationContext la classe che dobbiamo scrivere si chiama : ListEmployeeController omettiamo per brevità i metodi accessor e gli import statements:
package controllers; [ …import ….] public class ListEmployeeController extends AbstractController{ //sure! to be dependency injected private EmployeeInfoControl service ; […accessor methods …] protected ModelAndView handleRequestInternal(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception { //talk to service layer here .. Employee[] theList = service.getEmployeeInfo(); //Istantiating the view .. ModelAndView mav = new ModelAndView("list"); //adding the model to the view ... mav.addObject("employees", theList); //returning the view to the Dispatcher Servlet... return mav; } }
Come si vede qui facilmente il Controller chiede al service layer la lista degli Employee per poi passarla indietro alla Servlet tramite l’oggetto ModelAndView.
Il nome logico della View che dovrà essere renderizzata è list.
Per quanto riguarda lo strato di servizio è sufficiente definire una interfaccia e una classe : EmployeeInfoControlFakeImpl che implementa l’interfaccia EmployeeInfoControl.
package services; import java.io.Serializable; import model.*; public interface EmployeeInfoControl extends Serializable { public Employee[] getEmployeeInfo(); }
Per il nostro scopo sarà sufficiente una classe di implementazione fasulla che ritorna un array statico di oggetti Employee.
package services; import model.Employee; public class EmployeeInfoControlFakeImpl implements EmployeeInfoControl{ public Employee[] getEmployeeInfo() { // generate the array of employee to be returned [ ... ] Employee[] mockArray = new Employee[5]; Employee[] mockArray = new Employee[5]; mockArray[0] = getFake("massimo cimino", "via p. palla, 48 Rome", "roma", "italia", "mymail@gmail.com", 1, "067197xxx","00100") ; mockArray[1] = getFake("mara marzocchi", "via p. palla, 48 Rome", "roma", "italia", "mymail@gmail.com", 2, "067197xxx","00100") ; mockArray[2] = getFake("davide del vecchio", "via p. palla, 48 Rome", "roma", "italia", "mymail@gmail.com", 3, "067197xxx","00100") ; mockArray[3] = getFake("luisa picconi", "via p. palla, 48 Rome", "roma", "italia", "mymail@gmail.com", 4, "067197xxx","00100") ; mockArray[4] = getFake("marco immediato", "via p. palla, 48 Rome", "roma", "italia", "mymail@gmail.com", 5, "067197xxx","00100") ; return mockArray; } }
|
|
Il Model e la View
|
top
|
La classe Employee farà la funzione del model. Per il caso in esame la sua funzione è solo quella di incapsulare i dati anagrafici dei dipendenti :
package model; import java.io.Serializable; public class Employee implements Serializable { private static final long serialVersionUID = 1L;
private Integer id = 0; private String name = ""; private String address = ""; private String city = ""; private String state = ""; private String zip = ""; private String country = ""; private String email = ""; private String phone = ""; public Employee() { super(); }
public Employee( Integer ID, String name, String address, String city, String state, String zip, String country, String email, String phone ) { super(); this.id = ID; this.name = name == null ? "" : name; this.address = address == null ? "" : address; this.city = city == null ? "" : city; this.state = state == null ? "" : state; this.zip = zip == null ? "" : zip; this.country = country == null ? "" : country; this.email = email == null ? "" : email; this.phone = phone == null ? "" : phone; } […metodi accessor...] }
Dove per semplicità abbiamo omesso i metodi accessor degli attributi. Per quanto riguarda la View tutto quello che ci serve è una JSP all’interno della quale dovremo creare una tabella html per la visualizzazione dei dati.
<%@ page language="java" contentType="text/html;charset=UTF-8"%> <%@ page import="model.Employee"%> <html> <body> <table border="1"> <tr bgcolor="green"> <th>nome</th> <th>indirizzo</th> <th>citta'</th> <th>paese</th> <th>email</th> </tr>
<tr> <td>${requestScope.employees[0].name}</td> <td>${requestScope.employees[0].address}</td> <td>${requestScope.employees[0].city}</td> <td>${requestScope.employees[0].country}</td> <td>${requestScope.employees[0].email}</td> </tr>
<tr> <td>${requestScope.employees[1].name}</td> <td>${requestScope.employees[1].address}</td> <td>${requestScope.employees[1].city}</td> <td>${requestScope.employees[1].country}</td> <td>${requestScope.employees[1].email}</td> </tr> <tr> <td>${requestScope.employees[2].name}</td> <td>${requestScope.employees[2].address}</td> <td>${requestScope.employees[2].city}</td> <td>${requestScope.employees[2].country}</td> <td>${requestScope.employees[2].email}</td> </tr>
<tr> <td>${requestScope.employees[3].name}</td> <td>${requestScope.employees[3].address}</td> <td>${requestScope.employees[3].city}</td> <td>${requestScope.employees[3].country}</td> <td>${requestScope.employees[3].email}</td> </tr> <tr> <td>${requestScope.employees[4].name}</td> <td>${requestScope.employees[4].address}</td> <td>${requestScope.employees[4].city}</td> <td>${requestScope.employees[4].country}</td> <td>${requestScope.employees[4].email}</td> </tr> </table>
</body> </html>
Non rimane che aprire un browser e chiamare la URL mappata con il nostro controller e dovremmo ottener il risultato mostrato nella figura sottostante : http://localhost:8080/ExampleSpring/listEmployee.htm

|
|
RESOURCES
|
top
|
- The Spring Framework - Reference Documentation - libro: Expert Spring MVC and Web Flow di Seth Ladd, Keith Donald, casa editrice Apress - Inversion of Control Containers and the Dependency Injection pattern
|
|
|
| |
| JavaPortal è ideato da: |
 |

Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati sotto licenza Creative Commons
|
|
|