Vous êtes sur la page 1sur 63

Spring MVC par l'exemple

- Partie 3 -

serge.tahe@istia.univ-angers.fr, avril 2006

springmvc - partie3, ST universit dAngers

1/63

1 Rappels
Nous poursuivons dans cet article le travail fait dans les prcdents :

Spring MVC par l'exemple partie 1 : [http://tahe.developpez.com/java/springmvc-part1]


Spring MVC par l'exemple partie 2 : [http://tahe.developpez.com/java/springmvc-part2]

Rappelons, en quelques lignes, o nous en tions. L'architecture d'une application Spring MVC est la suivante :
Couche Interface Utilisateur[ui]
utilisateur

DispatcherServlet
1

Couche mtier
[metier]

2
3

Controller
4

View

Couche d'accs
aux donnes
[dao]

Donnes

Modle Map
6

1.
2.

3.

4.

5.

6.
7.

le client fait une demande au contrleur. Celui-ci voit passer toutes les demandes des clients. C'est la porte d'entre de
l'application. C'est le C de MVC. Ici le contrleur est assur par une servlet gnrique :
org.springframework.web.servlet.DispatcherServlet
le contrleur principal [DispatcherServlet] fait excuter l'action demande par l'utilisateur par une classe implmentant
l'interface :
org.springframework.web.servlet.mvc.Controller
A cause du nom de l'interface, nous appellerons une telle classe un contrleur secondaire pour le distinguer du
contrleur principal [DispatcherServlet] ou simplement contrleur lorsqu'il n'y a pas d'ambigut. Le schma cidessus s'est content de reprsenter un contrleur particulier. Il y a en gnral plusieurs contrleurs, un par action.
le contrleur [Controller] traite une demande particulire de l'utilisateur. Pour ce faire, il peut avoir besoin de l'aide
de la couche mtier. Une fois la demande du client traite, celle-ci peut appeler diverses rponses. Un exemple
classique est :

une page d'erreurs si la demande n'a pu tre traite correctement

une page de confirmation sinon


le contrleur choisit la rponse (= vue) envoyer au client. Choisir la rponse envoyer au client ncessite plusieurs
tapes :

choisir l'objet qui va gnrer la rponse. C'est ce qu'on appelle la vue V, le V de MVC. Ce choix dpend en
gnral du rsultat de l'excution de l'action demande par l'utilisateur.

lui fournir les donnes dont il a besoin pour gnrer cette rponse. En effet, celle-ci contient le plus souvent
des informations calcules par la couche mtier ou le contrleur lui-mme. Ces informations forment ce qu'on
appelle le modle M de la vue, le M de MVC. Spring MVC fournit ce modle sous la forme d'un dictionnaire
de type java.util.Map.
L'tape 4 consiste donc en le choix d'une vue V et la construction du modle M ncessaire celle-ci.
le contrleur DispatcherServlet demande la vue choisie de s'afficher. Il s'agit d'une classe implmentant l'interface
org.springframework.web.servlet.View
Spring MVC propose diffrentes implmentations de cette interface pour gnrer des flux HTML, Excel, PDF, ... Le
schma ci-dessus s'est content de reprsenter une vue particulire. Il y a en gnral plusieurs vues.
le gnrateur de vue View utilise le modle Map prpar par le contrleur Controller pour initialiser les parties
dynamiques de la rponse qu'il doit envoyer au client.
la rponse est envoye au client. La forme exacte de celle-ci dpend du gnrateur de vue. Ce peut tre un flux
HTML, PDF, Excel, ...

Nous avons vu dans la partie 1 de l'article les points suivants :

les diffrentes stratgies de rsolutions d'URL qui associent une URL demande par le client, un contrleur
implmentant l'interface [Controller] qui va traiter la demande du client (1,2)
les diffrentes faons qu'avait un contrleur [Controller] d'accder au contexte de l'application, par exemple pour
accder aux instances des couches [mtier] et [dao]

springmvc - partie3, ST universit dAngers

2/63

les diffrentes stratgies de rsolutions de noms de vue qui, un nom de vue rendu par le contrleur [Controller]
associe une classe implmentant l'interface [View] charge d'afficher le modle [Map] construit par le contrleur
[Controller] (4,5)

Nous avons vu dans la partie 2 de l'article les points suivants :

les classes [HandlerInterceptor] qui filtrent la requte avant de la passer au [Controller] qui doit la traiter (2)

les diffrentes faons de grer l'internationalisation (localisation) d'une application web

les gestionnaires des exceptions qui peuvent de produire dans une application web

la gestion des formulaires grce la classe [SimpleFormController]


Dans le prsent article, nous prsentons :

quelques implmentations de l'interface [Controller] non encore abordes

des implmentations de l'interface [View] permettant de gnrer des documents PDF et Excel

des outils facilitant l'criture de formulaires destins tlcharger des documents du poste client vers le serveur
Lapplication exemple qui devait tre prsente dans cet article le sera dans le prochain.

2 Encore des contrleurs


Revenons sur le schma de base d'une architecture Spring MVC :
Couche Interface Utilisateur[ui]
utilisateur

DispatcherServlet

Intercepteur1
2

Controller
5

View

Intercepteur2

Intercepteur3

3
4

Mtier | DAO

Donnes

Modle Map
6

Au cours de l'tape 2, la requte [HttpRequest] du client va tre traite par les diffrents modules placs entre le contrleur
gnral [DispatcherServlet] et le contrleur particulier [Controller] qui doit traiter la demande du client. Nous nous proposons
ici de prsenter de nouveaux objets [Controller].

2.1

Le contrleur ParameterizableViewController

L'interface [Controller] est la suivante :

Parmi les classes implmentant cette interface, on voit ci-dessus, la classe [ParameterizableViewController]. Ce contrleur ne
fait aucun travail sur la requte qu'il reoit. Il se contente de dterminer la vue qui doit tre affiche. Celui-ci est gnralement
fix par configuration. Le contrleur [ParameterizableViewController] ne construit aucun modle pour la vue qu'il demande
d'afficher. Celle-ci doit donc rcuprer son modle, si elle en a un, dans les diffrents contextes de l'application : requte,
session, application.
La classe [ParameterizableViewController] est dfinie comme suit :

springmvc - partie3, ST universit dAngers

3/63

la mthode [handleRequestInternal] appele par [DispatcherServlet] pour crer un objet [ModelAndView] partir de
la requte courante, se contente de rendre le modle : new ModelAndView(getViewName()). Elle ne rend aucun
modle [Map].
La mthode [setViewName] permet de fixer la vue du [ModelAndView]

Pour illustrer l'usage de cette classe, nous utiliserons le projet Eclipse / Tomcat (spring-mvc-32) suivant :

Le fichier de configuration Spring (spring-mvc-32-servlet.xml) de l'application est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/springbeans.dtd">
<beans>
<!-- les mappings de l'application-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/accueil.html">ForwardController</prop>
</props>
</property>
</bean>
<!-- le rsolveur de vues -->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename">
<value>vues</value>
</property>
</bean>
<!-- les contrleurs de l'application-->
<bean id="ForwardController"
class="org.springframework.web.servlet.mvc.ParameterizableViewController">
<property name="viewName" value="forward"/>
</bean>
</beans>

springmvc - partie3, ST universit dAngers

4/63

lignes 5-11 : dfinissent les Url gres par l'application


ligne 8 : seule l'url /accueil.html est ici traite. Elle le sera par le contrleur d'id [ForwardController] dfini aux lignes
19-22.
lignes 19-22 : le contrleur est de type [ParameterizableViewController]. Le nom de la vue est fixe par la proprit
[viewName] ligne 21, forant Spring appeler la mthode [setViewName] de la classe. La vue s'appelera donc
forward. On se sait pas encore quoi elle correspond.
lignes 13-17 : dfinit le mode de rsolution des noms de vues. Ici le rsolveur est [ResourceBundleViewResolver]
avec l'attribut basename gal vues. Ceci signifie que la dfinition des vues sera trouve dans le fichier de proprits
[vues.properties] situ dans le ClassPath de l'application.

Le fichier [vues.properties] a t plac ici dans [WEB-INF/src]. Le projet Eclipse / Tomcat le recopie automatiquement dans
[WEB-INF/classes], le plaant ainsi dans le ClassPath de l'application. Son contenu est le suivant :
1.
2.
3.

#forward
forward.class=org.springframework.web.servlet.view.JstlView
forward.url=/WEB-INF/vues/forward.jsp

La vue associe au nom [forward] est une page JSP (ligne 3). Sa classe d'affichage est donc la classe [JstlView] (ligne 2).
La page JSP [forward.jsp] est la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.

<%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>


<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<%@ page isELIgnored="false" %>

la requte qui sera traite par le contrleur [ParameterizableViewController] sera de la


/forward.html?nom=XX&age=YY.
lignes 7-8 : on rcupre le paramtre [nom] de la requte. S'il est absent, on lui donne une valeur arbitraire.
lignes 9-10 : on fait de mme pour le paramtre [age]
lignes 18-19 : les deux valeurs prcdentes sont incluses dans la page

<%
// on rcupre les paramtres de la requte
String nom=request.getParameter("nom");
if(nom==null) nom="inconnu";
String age=request.getParameter("age");
if(age==null) age="inconnu";
%>
<html>
<head>
<title>Spring(mvc-32)</title>
</head>
<body>
<h3>ParameterizableViewController</h3>
Nom=<%=nom%><br>
Age=<%=age%><br>
</body>
</html>

forme

On pourra remarquer que dans le projet Eclipse, il n'y a pas de code Java. On se contente de ce que sait faire
[ParameterizableViewController].
Pour les tests, nous demandons l'url [http://localhost:8080/spring-mvc-32/accueil.html] :

ou encore l'url [http://localhost:8080/spring-mvc-32/accueil.html?nom=paul&age=55] :

springmvc - partie3, ST universit dAngers

5/63

2.2

Le contrleur UrlFileNameViewController

Revenons sur l'interface [Controller] :

Parmi les classes implmentant cette interface, on voit ci-dessus, la classe [UrlFileNameViewController]. Ce contrleur est trs
semblable au contrleur [ParameterizableViewController] si ce n'est que le nom de la vue afficher est extrait du dernier
lment de l'url demande, plutt que dfini dans un fichier de configuration. Ainsi si l'URL demande est de la forme
[http://machine:port/elmt1/elmt2/.../elmtn.xxx], le nom de la vue sera elmtn. A ce nom, peuvent tre ajouts un prfixe et un
suffixe qui, eux, sont dfinis par configuration.
La classe [UrlFileNameViewController] est dfinie comme suit :

les mthodes [setPrefix] et [setSuffix] permettent de dfinir le prfixe et le suffixe ajouter aux noms des vues. On
notera que ces deux mthodes sont apparues tardivement dans les distributions de Spring. Elles nexistaient par
exemple pas dans la distribution 1.2.4 que jutilisais jusqu maintenant dans ces articles. Dans la suite de larticle,
jutilise dsormais la version 1.2.7.

Pour illustrer l'usage de cette classe, nous utiliserons le projet Eclipse / Tomcat (spring-mvc-32B) suivant :
springmvc - partie3, ST universit dAngers

6/63

Le fichier de configuration Spring (spring-mvc-32B-servlet.xml) de l'application est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/springbeans.dtd">
<beans>
<!-- les mappings de l'application-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<entry key="/forward.html">
<ref local="urlFileNameViewController"/>
</entry>
</map>
</property>
</bean>
<!-- le rsolveur de vues -->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename">
<value>vues</value>
</property>
</bean>
<!-- les contrleurs de l'application-->
<bean id="urlFileNameViewController"
class="org.springframework.web.servlet.mvc.UrlFilenameViewController">
<property name="prefix">
<value>-</value>
</property>
<property name="suffix">
<value>-</value>
</property>
</bean>
</beans>

ligne 8 : seule l'URL [/forward.html] est accepte


ligne 9 : elle est traite par le contrleur d'id [urlFileNameViewController]. Ce contrleur est dfini lignes 21-29.
lignes 21-22 : le contrleur d'id [urlFileNameViewController] est de type [ UrlFilenameViewController ] (ligne 22).
ligne 23 : le signe - sera prfix au nom de la vue
ligne 27 : le signe - sera suffix au nom de la vue
des lignes prcdentes, il ressort que la demande de l'url [/forward.html] va entraner l'affichage de la vue portant le
nom [-forward-].

Les vues sont dfinies dans le fichier [WEB-INF/src/vues.properties] suivant :


1.
2.
3.

#-forward-forward-.class=org.springframework.web.servlet.view.JstlView
-forward-.url=/WEB-INF/vues/forward.jsp

On y dfinit l'unique vue de nom [-forward-]. C'est au final, la page JSP [/WEB-INF/vues/forward.jsp] qui sera affiche. C'est
la mme que dans l'exemple prcdent quelques dtails prs :
1. <%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>
2. <%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
3. <%@ page isELIgnored="false" %>
4.
5. <%
6.
// on rcupre les paramtres de la requte
7.
String nom=request.getParameter("nom");
springmvc - partie3, ST universit dAngers
7/63

8.
if(nom==null) nom="inconnu";
9.
String age=request.getParameter("age");
10. if(age==null) age="inconnu";
11. %>
12. <html>
13. <head>
14.
<title>Spring(mvc-32B)</title>
15. </head>
16. <body>
17.
<h3>UrlFileNameViewController</h3>
18.
Nom=<%=nom%><br>
19.
Age=<%=age%><br>
20. </body>
21. </html>

Pour tester, nous demandons l'url [http://localhost:8080/spring-mvc-32B/forward.html?nom=Hoeller&age=35] :

2.3
2.3.1

Le contrleur MultiActionController
Prsentation

Revenons sur l'interface [Controller] :

Parmi les classes implmentant cette interface, on voit ci-dessus, la classe [MultiActionController]. Cette classe permet de
traiter des requtes diffrentes avec un unique contrleur. Qu'entend-on par requtes diffrentes ? Une requte est de la forme
[/url?param1=val1&param2=val2&....]. [MultiActionController] distingue deux requtes soit par la composante [url], soit par
l'un des paramtres [parami] qui sert de marqueur de requte.
La classe [MultiActionController] est une classe abstraite destine tre drive. Sa ligne est la suivante :

[MultiActionController] drive de la classe abstraite [AbstractController], classe parent d'un certain nombre d'autres
contrleurs prdfinis de Spring MVC. Pour qu'elle soit vraiment utile, il faut la driver. Pour faciliter l'explication qui va
suivre, appelons [MyMultiActionController], une classe drive de [MultiActionController].
Deux requtes diffrentes [req1] et [req2] seront traites
[MyMultiActionController]. Celles-ci ont la signature suivante :

par

deux

mthodes

diffrentes

du

contrleur

ModelAndView actionName(HttpServletRequest request, HttpServletResponse response);


springmvc - partie3, ST universit dAngers

8/63

On retrouve une situation connue. La mthode ci-dessus doit exploiter la requte [request] et renvoyer [DispatchServlet] un
[ModelAndView] afficher. En fait, les mthodes [actionName] peuvent avoir deux autres signatures. Nous renvoyons le
lecteur au Javadoc de la classe [MultiActionController].
Les requtes traites par [MyMultiActionController] vont d'abord l'tre par des mthodes de la classe parent
[MultiActionController]. Parmi celles-ci, on trouve la suivante :

Si la valeur du 1er paramtre [methodName] est XX, la classe [MultiActionController] va appeler la mthode suivante de sa
classe drive [MyMultiActionController] :
ModelAndView

XX(HttpServletRequest

request, HttpServletResponse response);

Il nous reste savoir, comment [MultiActionController] connat le nom de la mthode qui doit traiter la requte particulire
[req1], par exemple. Elle utilise pour cela, un rsolveur de noms de mthodes, c.a.d. une classe qui sait associer une requte
reue la mthode qui doit la traiter. Une mthode de [MultiActionController] permet de fixer le rsolveur de noms de
mthodes utiliser :

Comme on peut s'y attendre avec Spring, [MethodNameResolver] est une interface :

L'interface [MethodNameResolver] n'a qu'une mthode :

La mthode [getHandlerMethodName] rend le nom de la mthode de [MultiActionController] qui doit tre appele pour traiter
la requte [request]. Ceci connu, [MultiActionController] va appeler cette mthode qui sera trouve dans sa classe drive
[MyMultiActionController].
Il y a plusieurs stratgies pour associer une requte [request] un nom de mthode. On voit ci-dessus, que Spring propose
quatre implmentations de l'interface [MethodNameResolver] :

[AbstractUrlMethodNameResolver] : classe abstraite. Sa mthode [getHandlerMethodName] fait appel la


mthode abstraite [String getHandlerMethodNameForUrlPath(String urlPath)] qui doit tre implmente dans une
classe drive. Cette mthode reoit le chemin de l'URL de la requte qui doit tre traite. A partir de cette
information, elle doit fournir le nom de la mthode du [MultiActionController] charge de traiter la requte.
[InternalPathMethodNameResolver] : implmentation par dfaut utilise par [MultiActionController]. Une requte
d'url [/elmt1/elmt2/.../elmntN.extension] est traite par la mthode du [MultiActionController] nomme [elmntN].
[ParameterMethodNameResolver] : cette implmentation supporte deux statgies de rsolution de noms de
mthodes :
1. un paramtre particulier de l'url de la requte fixe le nom de la mthode du [MultiActionController]
utiliser. Par dfaut, ce paramtre est action. Ainsi l'url /url?action=XX&param1=val1&... sera traite par
la mthode XX. La mthode [setParamName] permet de fixer le nom du paramtre qui dfinit la mthode
du [MultiActionController] utiliser si on veut un autre nom que [action].
1. le nom du paramtre est lui-mme le nom de la mthode utiliser. Sa valeur est ignore. Comme il peut y
avoir d'autres paramtres dans la requte, la mthode [setParamNames] sert fixer les paramtres qui sont
des noms de mthodes. Ainsi un formulaire ayant trois boutons de type Submit appels [submit1, submit2,
submit3] pourra tre trait par les mthodes : [submit1, submit2, submit3]. La mthode qui traitera la

springmvc - partie3, ST universit dAngers

9/63

2.3.2

requte dpendra du bouton utilis pour faire le Submit du formulaire. On utilisera la mthode
[setParamNames] pour indiquer que la prsence dun des paramtres [submit1, submit2, submit3] dans la
requte implique que celle-ci doit tre traite par la mthode du [MultiActionController] portant le nom de
ce paramtre.
[PropertiesMethodNameResolver] : l'association [requte] <-> [mthode] est faite par un objet [Properties] l'aide
de la mthode [setMappings(Properties props)].

Exemple 1

Pour illustrer l'usage de la classe [MultiActionController], nous utiliserons le projet Eclipse / Tomcat (spring-mvc-33) suivant :

L'application (spring-mvc-33) n'a qu'une vue :

Il s'agit de grer une liste de noms. Trois actions seront possibles :

lister tous les noms

supprimer de noms

ajouter de noms
Ces trois actions seront traites par un unique contrleur, d'o l'usage de la classe [MultiActionController].
Pour tre plus raliste, nous supposerons que la liste des noms est gre par une couche [mtier]. L'architecture de l'aplication
est la suivante :

springmvc - partie3, ST universit dAngers

10/63

Couche Interface Utilisateur[ui]


utilisateur

DispatcherServlet
1

Couche mtier
[metier]

2
3

MultiActionController
4

Modle Map

View

La couche [mtier] est dans le paquetage [istia.st.springmvc.exemples.metier] du projet :

[IMetier] est l'interface que la couche [mtier] prsente la couche [web] :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

package istia.st.springmvc.exemples.metier;

ligne 7 : la mthode [getAll] permet d'avoir les noms sous la forme d'un objet de type List
ligne 9 : la mthode [add] permet d'ajouter la liste actuelle des noms, une liste de nouveaux noms
ligne 11 : la mthode [delete] permet de supprimer certains noms de la liste actuelle des noms

import java.util.List;
public interface IMetier {
// liste de tous les noms
public List getAll();
// ajouter de nouveaux noms
public void add(List noms);
// supprimer certains noms
public void delete(List noms);
}

[MetierImpl] est une classe d'implmentation utilisant un objet [ArrayList] pour stocker les noms :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.

package istia.st.springmvc.exemples.metier;
import java.util.ArrayList;
import java.util.List;
public class MetierImpl implements IMetier {
// une liste de noms
private ArrayList noms;
public ArrayList getNoms() {
return noms;
}
public void setNoms(ArrayList noms) {
this.noms = noms;
}
// ajouter de nouveaux noms
public synchronized void add(List nouveaux) {
this.noms.addAll(nouveaux);
}
// supprimer certains noms
public synchronized void delete(List nomsAEnlever) {
this.noms.removeAll(nomsAEnlever);
}
// liste de tous les noms
public synchronized List getAll() {
return this.noms;
}
}

springmvc - partie3, ST universit dAngers

11/63

lignes 9-17 : la liste des noms avec ses mthodes get et set. La mthode set nous permettra d'initialiser la liste des
noms par configuration Spring.
parce que la classe [MetierImpl] va tre instancie en un unique exemplaire (singleton) qui sera partag entre tous les
utilisateurs de l'application, il nous faut grer les ventuels conflits d'accs l'objet [ArrayList noms]. Pour cette
raison, nous avons synchronis toutes les mthodes, assurant par l qu'un seul thread la fois peut oprer sur la liste
des noms.

La couche [mtier] dfinie, nous passons la couche web. Tout d'abord, examinons la configuration de l'application (springmvc-33).
On voit que dans le projet (paragraphe 2.3.2, page 10), il y a un fichier [applicationContext.xml]. Il va nous servir dfinir le
contexte de l'application et notamment l'implmentation de la couche [mtier] utiliser. Pour que ce fichier soit exploit, il faut
que [web.xml] charge le listener [org.springframework.web.context.ContextLoaderListener] (lignes 8-11 ci-dessous) :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.

<?xml version="1.0" encoding="ISO-8859-1"?>


<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- le chargeur du contexte spring de l'application -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- la servlet -->
<servlet>
<servlet-name>spring-mvc-33</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<!-- le mapping des url -->
<servlet-mapping>
<servlet-name>spring-mvc-33</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>

Le contexte de l'application est dfini comme suit dans [applicationContext.xml] :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

<?xml version="1.0" encoding="ISO_8859-1"?>


<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- couche mtier -->
<bean id="metier" class="istia.st.springmvc.exemples.metier.MetierImpl">
<property name="noms">
<list>
<value>paul</value>
<value>mlanie</value>
<value>joachim</value>
</list>
</property>
</bean>
</beans>

lignes 5-13 : dfinit le singleton de la couche [mtier] avec un bean d'id " metier "
ligne 5 : la classe d'implmentation de la couche [mtier]
lignes 6-12 : initialisation de l'attribut [noms] de cette classe. La mthode [setNoms] de la classe est ici implicitement
utilise.

Le fichier [spring-mvc-33-servlet.xml] configure la couche web de l'application :


1.
2.

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/springbeans.dtd">
3. <beans>
4.
<!-- les mappings de l'application-->
5.
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
6.
<property name="mappings">
7.
<props>
8.
<prop key="multiactions.html">MultiActionsController</prop>
9.
</props>
10.
</property>
11. </bean>
12. <!-- le rsolveur de vues -->
13. <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
14.
<property name="basename">
15.
<value>vues</value>
16.
</property>
springmvc - partie3, ST universit dAngers

12/63

17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.

</bean>
<!-- les contrleurs de l'application-->
<bean id="MultiActionsController"
class="istia.st.springmvc.exemples.web.MyMultiActionController">
<property name="methodNameResolver">
<ref local="myMethodNameResolver"/>
</property>
<property name="metier">
<ref bean="metier"/>
</property>
</bean>
<!-- le rsolveur des noms de mthodes -->
<bean id="myMethodNameResolver"
class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
<property name="defaultMethodName"><value>list</value></property>
<property name="paramName"><value>action</value></property>
</bean>
</beans>

ligne 8 : l'unique url accepte est [/multiactions.html]. Elle est traite par le contrleur d'id [MultiActionController]
dfini lignes 19-27.
ligne 20 : le contrleur d'id [MultiActionsController] est de type [MyMultiActionsControler]. Nous verrons
ultrieurement que ce type est driv du contrleur [MultiActionController].
lignes 21-23 : dfinissent le rsolveur de noms de mthodes du contrleur [MultiActionController]. Ce rsolveur d'id
"myMethodNameResolver" est dfini lignes 29-32.
lignes 24-26 : la rfrence de la couche mtier est injecte dans le contrleur
lignes 29-32 : dfinissent le rsolveur de noms de mthodes d'id "myMethodNameResolver"
ligne 29 : il est de type [ParameterMethodNameResolver]. Nous avons vu que ce rsolveur permettait deux stratgies
de rsolution de mthodes (cf page 9). Nous utilisons celle o le nom de la mthode utiliser est fix par la valeur
d'un paramtre particulier de la requte.
ligne 31 : ce paramtre est [action]. C'est en fait la valeur par dfaut qui est utilise lorsqu'aucun nom de paramtre
n'est dfini. Nous pouvions donc ne pas dfinir la proprit [paramName] de [ParameterMethodNameResolver].
ligne 30 : la proprit [defaultMethodName] de [ParameterMethodNameResolver] permet d'indiquer quelle mthode
excuter lorsque le paramtre [action] n'est pas trouv dans la requte. Ici, ce sera la mthode [list].
lignes 13-17 : le rsolveur des noms de vues, ici [ResourceBundleViewResolver]. Les vues sont dfinies dans le
fichier [WEB-INF/src/vues.properties] :
1.
2.
3.

#list
list.class=org.springframework.web.servlet.view.JstlView
list.url=/WEB-INF/vues/list.jsp

Il n'y a qu'un nom de vue : list, associ la page JSP [/WEB-INF/vues/list.jsp]. L'aspect visuel de cette vue a dj t
prsent :

5
6

Les composants du formulaire HTML sous-jacent sont les suivants :


N

Type HTML

Nom

Rle

<form>

frmNoms formulaire

<select multiple>

noms

<input type= "submit ">

<textarea>

liste actuelle des noms


pour supprimer les noms slectionns dans (2)

nouveaux liste de noms ajouter, raison d'un nom par

springmvc - partie3, ST universit dAngers

13/63

ligne
5

<input type= "submit ">

pour ajouter dans (2) les noms de (4)

<input type= "hidden "> action

sert prciser l'action faire.

Le code JSP de la page est le suivant :


1.

<%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO8859-1"%>


<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<%@ page isELIgnored="false" %>

2.
3.
4.
5. <html>
6.
<head>
7.
<title>Spring(mvc-33)</title>
8.
<!-- Javascript -->
9.
<script language="javascript">
10.
function submitAction(uneAction){
11.
with(document.frmNoms){
12.
action.value=uneAction;
13.
submit();
14.
}
15.
}
16.
</script>
17. </head>
18. <!-- HTML -->
19. <body>
20.
<h3>MultiActionController</h3>
21.
<form name="frmNoms" method="post">
22.
<table border="0">
23.
<tr>
24.
<td>Noms</td>
25.
</tr>
26.
<tr>
27.
<td>
28.
<select name="noms" multiple>
29.
<c:forEach items="${noms}" var="nom">
30.
<option>${nom}</option>
31.
</c:forEach>
32.
</select>
33.
</td>
34.
<td>
35.
<input type="button" value="Supprimer" onclick='submitAction("delete");'>
36.
</td>
37.
</tr>
38.
</table>
39.
<br>
40.
<table border="0">
41.
<tr>
42.
<td>Nouveaux noms</td>
43.
</tr>
44.
<tr>
45.
<td>
46.
<textarea name="nouveaux" rows="3"></textarea>
47.
</td>
48.
<td>
49.
<input type="button" value="Ajouter" onclick='submitAction("add");'>
50.
</td>
51.
</tr>
52.
</table>
53.
<input type="hidden" name="action">
54.
</form>
55. </body>
56. </html>

lignes 10-15 : un script javascript qui donne une valeur au champ cach [action] de la ligne 53 puis poste le
formulaire. Ce script est appel ligne 35 lorsque l'utilisateur appuie sur le bouton [Supprimer]. On voit
qu'alors, action=delete. Il est appel galement ligne 49 lorsque l'utilisateur appuie sur le bouton [Ajouter].
On voit qu'alors, action=add.
le POST du formulaire se fera donc avec un paramtre action prenant ses valeurs dans [add,delete]. On sait
que ce paramtre action sera utilis par le rsolveur de noms de mthodes pour dterminer quelle mthode
du contrleur traitera la requte. Lors de l'appel initial du formulaire via l'url [/multiactions.html], le
paramtre [action] sera absent. C'est alors la proprit [defaultMethodName] du rsolveur de noms de
mthodes qui sera utilise pour dterminer quelle mthode du contrleur traitera la requte. Par
configuration, (ligne 30 de spring-mvc-33-servlet.xml), ce sera la mthode list. Notre contrleur
[MultiActionController] devra donc avoir les mthodes [add, delete, list].

springmvc - partie3, ST universit dAngers

14/63

lignes 29-31 : les options de la liste sont trouves dans un attribut noms qui sera cherch dans les diffrents
contextes de l'application. Le contrleur [MultiActionController] devra donc placer cet attribut dans le
modle de la vue.

Nous avons dsormais les lments pour comprendre le code du contrleur [MyMultiActionController] :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.

package istia.st.springmvc.exemples.web;

ligne 16 : le contrleur [MyMultiActionController] drive de la classe [MultiActionController]

import
import
import
import

java.util.Arrays;
java.util.HashMap;
java.util.List;
java.util.Map;

import istia.st.springmvc.exemples.metier.IMetier;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
public class MyMultiActionController extends MultiActionController {
// couche mtier
IMetier metier;
public IMetier getMetier() {
return metier;
}
public void setMetier(IMetier metier) {
this.metier = metier;
}
// liste des noms
public ModelAndView list(HttpServletRequest request,
HttpServletResponse response) {
// on demande les noms la couche [mtier]
List noms = metier.getAll();
// on les met dans le modle de la vue
Map modle = new HashMap();
modle.put("noms", noms);
// on rend le nom de la vue et son modle
return new ModelAndView("list", modle);
}
// supprimer des noms
public ModelAndView delete(HttpServletRequest request,
HttpServletResponse response) {
// liste des noms slectionn dans la liste
String[] noms = request.getParameterValues("noms");
// on demande la couche [mtier] de les supprimer de la liste existante
metier.delete(Arrays.asList(noms));
// on rend le nom de la vue et son modle
return list(request,response);
}

// ajouter des noms


public ModelAndView add(HttpServletRequest request,
HttpServletResponse response) {
// chane des noms ajouter
String nouveaux = request.getParameter("nouveaux");
// ces noms sont mis dans un tableau
String[] nouveauxNoms = nouveaux.split("\r\n");
// on cre la liste des noms ajouter
ArrayList alNouveauxNoms = new ArrayList();
String nouveauNom;
for (int i = 0; i < nouveauxNoms.length; i++) {
// on ne garde le nom que s'il est non vide
nouveauNom = nouveauxNoms[i].trim();
if (nouveauNom.length() != 0) {
alNouveauxNoms.add(nouveauNom);
}
}
// on demande la couche [mtier] de les ajouter la liste existante
if (alNouveauxNoms.size() != 0) {
metier.add(alNouveauxNoms);
}
// on rend le nom de la vue et son modle
return list(request, response);
}

springmvc - partie3, ST universit dAngers

15/63

lignes 19-27 : rfrence sur la couche mtier de type IMetier. On notera qu'on utilise l'interface et non le type d'une
classe d'implmentation particulire. Nous avons vu que le champ [metier] tait initialis par (spring-mvc-33servlet.xml).
lignes 30-39 : la mthode list qui traite l'action list ou l'absence d'action
lignes 42-50 : la mthode delete qui traite l'action delete
lignes 53-63 : la mthode add qui traite l'action add

Commentons tout d'abord la mthode [list]. Elle est appele notamment lorsque l'utilisateur appelle directement l'url
[/multiactions.html]. Comme le paramtre [action] n'est pas prsent, c'est la mthode par dfaut [list] qui est charge de traiter
la requte. Il s'agit de faire afficher la page [list.jsp] avec la liste des noms. De ce qu'on a vu prcdemment lors de l'tude de
[list.jsp], la mthode doit construire un [ModelAndView] avec :

pour nom de vue : list (cf vue.properties)

dans le modle, la liste des noms associe un attribut nomm "noms".

ligne 33 : la liste des noms est demande la couche [mtier]


ligne 35 : un modle vide est construit
ligne 36 : la liste des noms est place dans le modle, associe un attribut nomm "noms"
ligne 38 : la mthode rend un [ModelAndView] avec " list " comme nom de vue et le modle qui vient d'tre construit.

La mthode [delete] (lignes 42-50) est appele lorsque l'utilisateur a appuy sur le bouton [Supprimer]. Il faut donc supprimer
de la liste des noms, les noms slectionns dans la liste HTML " noms " :

ligne 45 : on rcupre dans un tableau les noms slectionns dans la liste HTML appele "noms"
ligne 47 : on demande la couche [mtier] de les supprimer
ligne 49 : on veut rafficher la liste mise jour. Pour cela, on se contente de faire appel la mthode [list] qui sait
faire cela.

La mthode [add] (lignes 53-74) est appele lorsque l'utilisateur a appuy sur le bouton [Ajouter]. Il faut alors ajouter la liste
des noms, ceux qui ont t placs dans le <textarea> HTML appel "nouveaux" :

ligne 56 : on rcupre dans une chane le contenu du <textarea> HTML appel "nouveaux"
la valeur d'un <textarea> est une chane de type " ligne1\r\nligne2\r\nligne3... ", o lignei est une ligne de texte. Ligne
58, on rcupre les diffrentes lignes dans un tableau.
ligne 60 : on cre une liste vide dans laquelle on mettra les noms ajouter.
lignes 62-67 : on dbarrasse les noms des ventuels espaces qui peuvent les prcder ou les suivre et on ne garde le
nom que sil est non vide.
lignes 70-72 : on demande la couche [mtier] d'ajouter les nouveaux noms la liste actuelle
ligne 74 : on veut rafficher la liste mise jour. Pour cela, on se contente de faire appel la mthode [list].

Voici un exemple d'excution. On demande tout d'abord l'url [http://localhost:8080/spring-mvc-33/multiactions.html] :

On ajoute des noms :

springmvc - partie3, ST universit dAngers

16/63

requte
rponse
On supprime des noms :

rponse
requte

2.3.3

Exemple 2 (spring-mvc-33B)

Nous reprenons l'exemple prcdent dans un nouveau projet Eclipse (spring-mvc-33B). Celui-ci est identique au projet (springmvc-33) mais nous utilisons une autre stratgie de rsolution de noms de mthodes. La page [list.jsp] est modifie comme suit :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

<%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>


<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<%@ page isELIgnored="false" %>

le code Javascript a disparu ainsi que le champ cach nomm " action ".

<html>
<head>
<title>Spring(mvc-33B)</title>
</head>
<body>
<h3>MultiActionController (B)</h3>
<form method="post">
...
<input type="submit" name="delete" value="Supprimer">
...
<input type="submit" name="add" value="Ajouter">
...
</form>
</body>
</html>

springmvc - partie3, ST universit dAngers

17/63

les boutons [Supprimer] et [Ajouter] qui taient des composants HTML de type [Button] deviennent des composants
HTML de type [Submit]. Alors que prcdemment, ils n'avaient pas de noms, ils en ont dsormais un, respectivement
[delete] et [add].

La consquence principale de ces changements est dans les paramtres posts au serveur :

il n'y a plus de paramtre [action] post

lorsque le formulaire est post via le bouton [Supprimer], le paramtre [delete] sera post avec la valeur [Supprimer]

lorsque le formulaire est post via le bouton [Ajouter], le paramtre [add] sera post avec la valeur [Ajouter]
C'est ce que montrent l'outil [LiveHttpHeaders] dj utilis dans les articles prcdents :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

14.

POST /spring-mvc-33B/multiactions.html HTTP/1.1


Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US;
rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1
Accept:
text/xml,application/xml,application/xhtml+xml,text/html;q=0.
9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: fr,en;q=0.7,de;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer:
http://localhost:8080/spring-mvc33B/multiactions.html
Cookie: JSESSIONID=A83C308074C7E21EA2304993F2030A3D
Content-Type: application/x-www-form-urlencoded
Content-Length: 41

15. nouveaux=virginie%0D%0Asamuel&add=Ajouter

flux HTTP envoy par le navigateur client


demande

ci-dessus, ligne 15, le paramtre add est post.

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.

POST /spring-mvc-33B/multiactions.html HTTP/1.1


Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US;
rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1
Accept:
text/xml,application/xml,application/xhtml+xml,text/html;q=0.
9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: fr,en;q=0.7,de;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/spring-mvc33B/multiactions.html
Cookie: JSESSIONID=A83C308074C7E21EA2304993F2030A3D
Content-Type: application/x-www-form-urlencoded
Content-Length: 52

11.
12.
13.
14.
15. noms=virginie&noms=samuel&delete=Supprimer&nouveaux=

flux HTTP envoy par le navigateur client

demande

ci-dessus, ligne 15, le paramtre delete est post.

L'unique URL traite par l'application reste [/multiactions.html]. On a trois cas :

l'url ne contient aucun des paramtres [delete,add] : on veut que la mthode [list] du contrleur
[MultiActionController] soit excute.

l'url contient le paramtre [delete] ou [add] : on veut que la mthode [delete] ou [add] du contrleur
[MultiActionController] soit excute.
Ce fonctionnement est obtenu en utilisant le rsolveur de noms de mthodes suivant (spring-mvc-33-servlet.xml) :
springmvc - partie3, ST universit dAngers

18/63

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/springbeans.dtd">
<beans>
....
<!-- les contrleurs de l'application-->
<bean id="MultiActionsController"
class="istia.st.springmvc.exemples.web.MyMultiActionController">
<property name="methodNameResolver">
<ref local="myMethodNameResolver"/>
</property>
<property name="metier">
<ref bean="metier"/>
</property>
</bean>
<!-- le rsolveur des noms de mthodes -->
<bean id="myMethodNameResolver"
class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
<property name="defaultMethodName"><value>list</value></property>
<property name="methodParamNames">
<list>
<value>delete</value>
<value>add</value>
</list>
</property>
</bean>
</beans>

ligne 16 : le rsolveur de noms de mthodes est comme dans l'exemple prcdent [ParameterMethodNameResolver]
ligne 17 : lorsque le rsolveur de noms de mthodes ne russit pas lier une requte une mthode, c'est la mthode
[list] qui sera utilise.
lignes 18-23 : dfinissent les paramtres qui, lorsque leur seule prsence est dtecte dans la requte, provoque
l'excution de la mthode qui porte leur nom.

Le lecteur est invit tester cette nouvelle version.

2.3.4

Exemple 3 (spring-mvc-33C)

Nous reprenons le mme exemple dans un nouveau projet Eclipse (spring-mvc-33C). Nous utilisons une autre stratgie de
rsolution de noms de mthodes (spring-mvc-33-servlet.xml) :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/springbeans.dtd">
<beans>
<!-- les mappings de l'application-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/list.html">MultiActionsController</prop>
<prop key="/delete.html">MultiActionsController</prop>
<prop key="/add.html">MultiActionsController</prop>
</props>
</property>
</bean>
<!-- le rsolveur de vues -->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename">
<value>vues</value>
</property>
</bean>
<!-- les contrleurs de l'application-->
<bean id="MultiActionsController"
class="istia.st.springmvc.exemples.web.MyMultiActionController">
<property name="metier">
<ref bean="metier"/>
</property>
</bean>
</beans>

lignes 8-10 : l'application admet dsormais trois URL


lignes 21-26 : le contrleur est toujours le mme que prcdemment mais aucun rsolveur de noms de mthodes n'est
dfini. Dans ce cas, c'est le rsolveur [InternalPathMethodNameResolver] qui est utilis. Avec ce rsolveur, une URL
[/M.html] est traite par la mthode [M]. Ainsi, l'url [/list.html] sera traite par la mthode [list], l'url [/delete.html] par
la mthode [delete], l'url [/add.html] par la mthode [add].

La page [list.jsp] est transforme comme suit :


springmvc - partie3, ST universit dAngers

19/63

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.

...
<html>
<head>
<title>Spring(mvc-33C)</title>
</head>
<body>
<h3>MultiActionController (C)</h3>
<form method="post" action="<c:url value="/delete.html"/>">
...
<select name="noms" multiple>
...
</select>
...
<input type="submit" name="delete" value="Supprimer">
...
</form>
<br>
<form method="post" action="<c:url value="/add.html"/>">
...
<textarea name="nouveaux" rows="3"></textarea>
...
<input type="submit" name="add" value="Ajouter">
...
</form>
</body>
</html>

La page comporte dsormais deux formulaires :

le 1er formulaire est dfini lignes 8-16 et sert poster les noms supprimer

ligne 8 : les paramtres seront posts l'url [/delete.html]. Comme nous l'avons vu, cela va entraner qu'ils seront
traits par la mthode [delete] du contrleur.

le second formulaire est dfini lignes 18-24 et sert poster les noms ajouter

ligne 18 : les paramtres seront posts l'url [/add.html]. Comme nous l'avons vu, cela va entraner qu'ils seront traits
par la mthode [add] du contrleur.
Pour demander la liste initiale des noms, il faut demander l'url [/list.html] comme montr ci-dessous :

Le lecteur est invit tester cette nouvelle version.

2.3.5

Exemple 4 (spring-mvc-33D)

Nous reprenons le mme exemple dans un nouveau projet Eclipse (spring-mvc-33D). Nous utilisons une autre stratgie de
rsolution de noms de mthodes (spring-mvc-33-servlet.xml) :
1.
2.
3.
4.
5.
6.
7.
8.
9.

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/springbeans.dtd">
<beans>
<!-- les mappings de l'application-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/list.html">MultiActionsController</prop>
<prop key="/delete.html">MultiActionsController</prop>

springmvc - partie3, ST universit dAngers

20/63

10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.

<prop key="/add.html">MultiActionsController</prop>
</props>
</property>
</bean>
<!-- le rsolveur de vues -->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename">
<value>vues</value>
</property>
</bean>
<!-- les contrleurs de l'application-->
<bean id="MultiActionsController"
class="istia.st.springmvc.exemples.web.MyMultiActionController">
<property name="methodNameResolver">
<ref local="myMethodNameResolver"/>
</property>
<property name="metier">
<ref bean="metier"/>
</property>
</bean>
<!-- le rsolveur des noms de mthodes -->
<bean id="myMethodNameResolver"
class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
<property name="mappings">
<props>
<prop key="/list.html">list</prop>
<prop key="/delete.html">delete</prop>
<prop key="/add.html">add</prop>
</props>
</property>
</bean>
</beans>

lignes 8-10 : l'application admet les mmes trois URL que prcdemment
lignes 31-39 : le rsolveur de noms de mthodes est dsormais [PropertiesMethodNameResolver]. Ce rsolveur
permet de dfinir, une une, les associations URL <-> mthode, grce sa proprit [mappings] (ligne 32). Lignes
34-36, nous associons chacune des URL traites par le contrleur, le nom de la mthode du contrleur qui doit la
traiter.

Le lecteur est invit tester cette nouvelle version.

2.4
2.4.1

Le contrleur AbstractWizardFormController
Prsentation

Revenons sur l'interface [Controller] :

Parmi les classes implmentant cette interface, on voit ci-dessus, la classe [AbstractWizardFormController]. Cette classe
permet de construire des formulaires forms de plusieurs pages. Ce type d'application est parfois appele assistant (wizard) d'o
le nom de la classe. Celle-ci est abstraite et doit donc tre drive. La classe [AbstractWizardFormController] fournit la
" mcanique " de gestion de l'assistant, la classe drive se contenant de traiter les parties spcifiques lassistant particulier
que lon construit.
Avant d'tudier la classe [AbstractWizardFormController], montrons des copies d'cran de l'application exemple que nous
allons construire. L'assistant aura trois pages :

la page n 0 demandera un nom. Celui-ci devra tre non vide pour tre accept.

la page n 1 demandera un ge. Celui-ci devra tre un nombre entier dans l'intervalle [1,150] pour tre accept.

la page n 2 demandera l'utilisateur de valider ces deux saisies.

l'utilisateur passe d'une page l'autre avec le bouton [Suivant].

tant qu'il n'a pas valid la dernire page de l'assistant (page n 2), l'utilisateur peut revenir en arrire (jusqu' la
page 0) avec le bouton [Prcdent]. Il retrouve chaque fois les valeurs saisies prcdemment.

tout moment (pages n 0 2), l'utilisateur peut annuler l'assistant avec le bouton [Dbut]
springmvc - partie3, ST universit dAngers
21/63

cette page ne fait pas partie


de l'assistant

page n 0 de nom " page1 "

page n 2 de nom " page3 "

page n 1 de nom " page2 "

cette page ne fait pas partie de l'assistant

Le projet Eclipse / Tomcat de cette application est le suivant :

L'application est configure par [web.xml] :


1. <?xml version="1.0" encoding="ISO-8859-1"?>
2.
3. <!DOCTYPE web-app PUBLIC
4.
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
5.
"http://java.sun.com/dtd/web-app_2_3.dtd">
6. <web-app>
7.
<!-- le chargeur du contexte spring de l'application -->
8.
<listener>
9.
<listener-class>
10.
org.springframework.web.context.ContextLoaderListener</listener-class>
11. </listener>
12. <!-- la servlet -->
13. <servlet>
springmvc - partie3, ST universit dAngers

22/63

14.
<servlet-name>spring-mvc-34</servlet-name>
15.
<servlet-class>
16.
org.springframework.web.servlet.DispatcherServlet</servlet-class>
17. </servlet>
18. <!-- le mapping des url -->
19. <servlet-mapping>
20.
<servlet-name>spring-mvc-34</servlet-name>
21.
<url-pattern>*.html</url-pattern>
22. </servlet-mapping>
23. <!-- fichier d'index -->
24. <welcome-file-list>
25.
<welcome-file>index.htm</welcome-file>
26. </welcome-file-list>
27. </web-app>

Il y a une nouveaut par rapport aux fichiers [web.xml] des applications prcdentes :

lignes 24-26 : dfinissent un fichier d'accueil. Lorsque l'url [/spring-mvc-24] sera demande, le serveur transformera
l'url en [/spring-mvc-24/index.htm]. [index.htm] est plac la racine du dossier [spring-mvc-24] (cf projet Eclipse).
C'est une page HTML qui contient le lien qui va lancer l'assistant :

Les lignes 9-11 de [web.xml] charge le listener capable dexploiter le contenu du fichier [applicationContext.xml]. Celui-ci est
le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.

<?xml version="1.0" encoding="ISO_8859-1"?>


<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- le fichier des messages -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>messages</value>
</property>
</bean>
</beans>

Il dfinit un fichier de messages sur lequel nous aurons l'occasion de revenir.


Le fichier (spring-mvc-34-servlet.xml) qui configure l'application Spring est le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/springbeans.dtd">
<beans>
<!-- les mappings de l'application-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="assistant.html">AssistantController</prop>
</props>
</property>
</bean>
<!-- le rsolveur de vues -->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename">
<value>vues</value>
</property>
</bean>
<!-- le contrleur de l'application-->
<bean id="AssistantController"
class="istia.st.springmvc.exemples.web.AssistantController">
<property name="sessionForm">
<value>true</value>
</property>
<property name="commandClass">
<value>istia.st.springmvc.exemples.web.Formulaire</value>
</property>
<property name="commandName">
<value>formulaire</value>
</property>
<property name="pages">
<list>
<value>page1</value>
<value>page2</value>
<value>page3</value>
</list>

springmvc - partie3, ST universit dAngers

23/63

36.
</property>
37.
<property name="allowDirtyBack">
38.
<value>true</value>
39.
</property>
40.
<property name="allowDirtyForward">
41.
<value>false</value>
42.
</property>
43. </bean>
44. </beans>

ligne 8 : l'unique URL traite sera [/assistant.html]. Elle le sera par le contrleur d'id [AssistantController]. Celui-ci est
dfini lignes 19-43. La classe [AssistantController] drivera de la classe [AbstractWizardFormController].

La classe [AbstractWizardFormController] a l'ascendance suivante :

La classe [AbstractWizardFormController] drive de la classe [AbstractFormController]. C'tait dj le cas de la classe


[SimpleFormController] que nous avons utilise dans l'article prcdent pour grer des formulaires mono-page. Aussi allonsnous trouver des similarits dans la configuration de ces deux types de contrleur. Celle du contrleur utilis dans l'application
exemple est la suivante :
1.
<!-- le contrleur de l'application-->
2.
<bean id="AssistantController"
3.
class="istia.st.springmvc.exemples.web.AssistantController">
4.
<property name="sessionForm">
5.
<value>true</value>
6.
</property>
7.
<property name="commandClass">
8.
<value>istia.st.springmvc.exemples.web.Formulaire</value>
9.
</property>
10.
<property name="commandName">
11.
<value>formulaire</value>
12.
</property>
13.
<property name="pages">
14.
<list>
15.
<value>page1</value>
16.
<value>page2</value>
17.
<value>page3</value>
18.
</list>
19.
</property>
20.
<property name="allowDirtyBack">
21.
<value>true</value>
22.
</property>
23.
<property name="allowDirtyForward">
24.
<value>false</value>
25.
</property>
26. </bean>

lignes 7-9 : dfinissent l'attribut [commandClass] qui fixe la classe qui doit tre instancie pour recevoir les saisies de
l'utilisateur. Si cet attribut n'est pas dfini, alors c'est la mthode [formBackingObject] qui doit fournir ce conteneur
des saisies (ce ne sera pas le cas ici). Le conteneur mmorisera le nom et l'ge saisis. Il est ici de type [Formulaire].
lignes 4-6 : dfinissent l'attribut [sessionForm] qui dcide du maintien ou non en session de l'objet [Formulaire] entre
les diffrentes pages. Si on veut permettre l'utilisateur de revenir en arrire dans l'assistant et qu'il retrouve les
valeurs qu'il a prcdemment saisies, on mettra l'attribut [sessionForm] vrai.
lignes 10-12 : le nom de l'attribut utiliser dans les pages JSP / JSTL pour avoir accs l'objet [Formulaire],
conteneur des saisies. Ici, ce sera [formulaire].

Les attributs prcdents avaient dj t rencontrs dans la configuration de [SimpleFormController]. Les attributs dfinis
lignes 13-25 sont propres au contrleur [AbstractWizardFormController] :

lignes 13-19 : l'attribut [pages] dfinit les noms des vues formant l'assistant. Ici l'assistant est form de trois vues
appeles respectivement : page1, page2, page3. Ces noms de vues seront associs des page JSP / JSTL grce au
rsolveur de noms de vues.

springmvc - partie3, ST universit dAngers

24/63

lignes 20-22 : l'attribut [allowDirtyBack] indique si l'utilisateur pourra ou non revenir en arrire alors mme que les
saisies dans la page courante sont invalides. Si la proprit [allowDirtyBack] est vrai, alors l'assistant nglige les
valeurs postes par la page courante et fait afficher la page prcdente . Si elle est faux, alors l'assistant vrifie les
valeurs postes par la page courante. Si elles sont incorrectes, la page courante est raffiche. La valeur par dfaut de
la proprit [allowDirtyBack] est la valeur true
lignes 23-25 : l'attribut [allowDirtyForward] indique si l'utilisateur peut passer la page suivante alors mme que les
saisies dans la page courante sont invalides. Si la proprit [allowDirtyForward] est vrai, alors l'assistant nglige les
valeurs postes par la page courante et fait afficher la page suivante. Si elle est faux, alors l'assistant vrifie les
valeurs postes par la page courante. Si elles sont incorrectes, la page courante est raffiche. La valeur par dfaut de
la proprit [allowDirtyForward] est la valeur false.

Les lignes 13-19 ne prcisent que les noms des vues formant l'assistant. L'association du nom de la vue sa classe d'affichage
est faite par le rsolveur de noms de vues, ici [ResourceBundleViewResolver] :
1. <!-- le rsolveur de vues -->
2. <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
3.
<property name="basename">
4.
<value>vues</value>
5.
</property>
6.</bean>

Le fichier [vues.properties] (ligne 4) est dfini dans le dossier [WEB-INF/src] comme suit :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.

#page1
page1.class=org.springframework.web.servlet.view.JstlView
page1.url=/WEB-INF/vues/page1.jsp
#page2
page2.class=org.springframework.web.servlet.view.JstlView
page2.url=/WEB-INF/vues/page2.jsp
#page3
page3.class=org.springframework.web.servlet.view.JstlView
page3.url=/WEB-INF/vues/page3.jsp
#index
index.class=org.springframework.web.servlet.view.JstlView
index.url=/index.htm
#validation
validation.class=org.springframework.web.servlet.view.JstlView
validation.url=/WEB-INF/vues/validation.jsp

Cinq vues JSP / JSTL sont dfinies :

les trois vues [page1, page2, page3] qui forment l'assistant

la vue [index] qui contient le lien partir duquel on lancera l'assistant

la vue [validation] qui va confirmer les saisies faites dans l'assistant


Le cheminement normal de l'utilisateur dans ces vues est le suivant : index -> page1 -> page2 -> page3 -> validation
Le comportement de l'assistant est gouvern par la prsence de certains paramtres dans la requte qu'il traite. La valeur de
ceux-ci n'a aucune importance. Seule importe leur prsence. Parmi ces paramtres on trouve les suivants :

_page : le n de la page dont on demande la validation. L'assistant va vrifier la validit des saisies de la page n
[_page] en excutant la mthode
protected void validatePage(Object formulaire, Errors errors, int page)

formulaire : le conteneur des saisies. A noter qu'il contient les saisies faites dans l'ensemble des pages de
l'assistant et non seulement les saisies de la page n [_page].

page : le n de page courant de l'assistant. Avec cette information, la mthode validatePage pourra ne
vrifier la validit que des seules saisies de cette page particulire.

errors : la liste des erreurs rencontres. Vide au dpart, elle doit tre remplie par la mthode validatePage
au fur et mesure que celle-ci dtecte des donnes invalides. Si, l'issue de la mthode validatePage, cette
liste est non vide, la page invalide est raffiche. On s'arrange en gnral pour qu'elle affiche outre les valeurs
prcdemment saisies, les messages des erreurs dtectes.
_targetx : x est un n de page commenant 0. La prsence de ce paramtre dans la requte indique l'assistant qu'il
doit passer la page n x. Il s'agit dans l'assistant, de passer de la page n [_page] la page n [x]. Ceci ne sera
possible que si la page n [_page] n'a pas t dclare invalide par la mthode [validatePage].
La prsence du paramtre [_target1] indique ainsi l'assistant qu'il doit passer la page n 1. Dans notre exemple, le
tableau des pages est [page1,page2,page3]. Les pages sont numrotes partir de 0. Ainsi la page n 1 est-elle la vue
" page2 " et donc la page JSP / JSTL [page2.jsp] (cf vues.properties).

springmvc - partie3, ST universit dAngers

25/63

_cancel : la prsence de cet attribut doit tre interprte comme une demande de sortie de l'assistant avec abandon des
saisies dj faites. A la rencontre de ce paramtre, l'assistant va excuter la mthode [processCancel]. Cette mthode
doit rendre un [ModelAndView] traduisant l'abandon de l'assistant.
_finish : la prsence de cet attribut doit tre interprte comme une demande de sortie de l'assistant avec validation
des saisies faites. A la rencontre de ce paramtre, l'assistant va excuter la mthode [processFinish]. Cette mthode
doit rendre un [ModelAndView] traduisant la validation des saisies. Cependant avant d'excuter cette mthode,
l'assistant fait une validation de toutes les pages de l'assistant, c.a.d. qu'il appelle la mthode validatePage pour
chacune des pages du tableau des pages. Si la validation des pages a t faite comme expliqu prcdemment, cela
peut ressembler redondant. En effet, il semble que si on est arriv la dernire page de l'assistant c'est que les
prcdentes taient correctes. Ce fonctionnement n'est cependant pas le seul possible. Un fonctionnement diffrent qui
permet d'avancer dans l'assistant sans vrification des donnes est galement possible (allowDirtyForward=true).
Celles-ci sont alors toutes vrifies d'un seul coup lorsque le paramtre _finish est reu.

Prenons un exemple. La 2ime page de notre assistant est la vue " page2 " suivante :

le bouton [Suivant] doit servir passer la page suivante de l'assistant. Parmi les paramtres posts, il faudra mettre
les suivants :

_page=1 : pour reflter le fait que la page courante " page2 " est la page d'indice 1 dans le tableau des pages
[page1, page2, page3] de l'assistant.

_target2 : pour reflter le fait que, si la page courante est valide, on veut passer la page d'indice 2 dans le
tableau des pages [page1, page2, page3] de l'assistant, c.a.d la vue " page3 ".

Cela peut tre obtenu avec le code HTML suivant :


1.
2.
3.
4.
5.
6.
7.

<form method="post">
...
<input type="hidden" name="_page" value="1">
...
<input type="submit" value="Suivant" name="_target2"></td>
...
</form>

le bouton [Prcdent] doit servir revenir la page prcdente de l'assistant. Parmi les paramtres posts, il faudra
mettre les suivants :

_page=1 : pour reflter le fait que la page courante " page2 " est la page d'indice 1 dans le tableau des pages
[page1, page2, page3] de l'assistant.

_target0 : pour reflter le fait qu'on veut passer la page d'indice 0 dans le tableau des pages [page1, page2,
page3] de l'assistant, c.a.d la vue " page1 ". A noter qu'alors la validation de la page [_page] n'aura pas lieu
(allowDirtyBack=true) et les saisies de la page courante seront abandonnes.

Cela peut tre obtenu avec le code HTML suivant :


1.
2.
3.
4.
5.
6.
7.

<form method="post">
...
<input type="hidden" name="_page" value="1">
...
<input type="submit" value="Prcdent" name="_target0"></td>
...
</form>

le bouton [Dbut] doit permettre d'abandonner l'assistant. Parmi les paramtres posts, il faudra mettre le suivant :

_cancel : pour reflter le fait que l'utilisateur veut abandonner l'assistant.

Cela peut tre obtenu avec le code HTML suivant :


1.
2.
3.
4.
5.
6.

<form method="post">
...
<input type="hidden" name="_page" value="1">
...
<input type="submit" value="Dbut" name="_cancel"></td>
...

springmvc - partie3, ST universit dAngers

26/63

7. </form>

Considrons maintenant la dernire page de l'assistant :

Le bouton [Valider] doit provoquer la sortie de l'assistant. Il faut donc mettre le paramtre _finish dans les paramtres posts.
Cela peut tre obtenu avec le code HTML suivant :
1.
<form method="post">
2.
<table>
3.
<tr>
4.
<td><input type="submit" value="Prcdent" name="_target1"></td>
5.
<td><input type="submit" value="Dbut" name="_cancel"></td>
6.
<td><input type="submit" value="Valider" name="_finish"></td>
7.
</tr>
8.
</table>
9. </form>

Ici, le paramtre _page est inutile car il n'y a aucune validation de donnes faire.

2.4.2

Le conteneur des saisies

Au fil des pages de l'assistant, les saisies seront enregistres dans une instance de la classe [Formulaire] suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

package istia.st.springmvc.exemples.web;
public class Formulaire {
// nom
private String nom;
// age
private int age;
// getters - setters
....
}

Lors de l'affichage de la premire page de l'assistant, une instance de cette classe est cre. Elle va rester ensuite en session
jusqu' la fin de l'assistant. Elle sera place dans le modle de chacune des vues [page1, page2, page3] avant affichage de
celles-ci sous le nom " formulaire ". Elle servira mmoriser les donnes saisies lors des POST des vues [page1, page2]. Ce
fonctionnement dcoule de la configuration du contrleur faite dans (spring-mvc-34-servlet.xml, page 23) et rappele cidessous :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.

<!-- les contrleurs de l'application-->

<bean id="AssistantController"
class="istia.st.springmvc.exemples.web.AssistantController">
<property name="sessionForm">
<value>true</value>
</property>
<property name="commandClass">
<value>istia.st.springmvc.exemples.web.Formulaire</value>
</property>
<property name="commandName">
<value>formulaire</value>
</property>
<property name="pages">
<list>
<value>page1</value>
<value>page2</value>
<value>page3</value>
</list>
</property>
<property name="allowDirtyBack">
<value>true</value>
</property>
<property name="allowDirtyForward">
<value>true</value>
</property>
</bean>

springmvc - partie3, ST universit dAngers

27/63

2.4.3

Les vues du projet

Les vues du projet sont dans le dossier [vues] du projet ainsi que sous la racine du projet (index.htm) :

L'application dmarre par la demande de l'url [/spring-mvc-34]. Nous avons vu que cela allait se traduire par la demande de
l'url [/spring-mvc-34/index.htm] (cf web.xml page 22). La page obtenue est la suivante :

Le code de cette page est le suivant :


1.
2.
3.
4.
5.
6.
7.

<html>
<head>
<title>Assistant</title>
</head>
<body>
<a href="assistant.html">Commencer l'assistant</a>
</body>

8. </html>

Le lien pointe sur l'url [assistant.html]. D'aprs l'tude du fichier [ spring-mvc-34-servlet.xml ] (page 23), nous savons que cette
url va tre traite par l'assistant [ AbstractWizardFormController ]. Parce que c'est un GET, l'assistant va afficher la premire
page, c.a.d. la vue " page1 " associe la page JSP [page1.jsp] :

Le code de [page1.jsp] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.

<%@
<%@
<%@
<%@

page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>


taglib uri="/WEB-INF/c.tld" prefix="c" %>
taglib uri="/WEB-INF/spring.tld" prefix="spring" %>
page isELIgnored="false" %>

<html>
<head>
<title>Spring(mvc-34)</title>
</head>
<body>
<h3>AssistantController (1/3) : saisie du nom</h3>
<form method="post">
<table border="0">
<tr>
<td>Votre nom :</td>
<spring:bind path="formulaire.nom">
<td>
<input type="text" name="${status.expression}" value="${status.value}">
</td>
<td>${status.errorMessage}</td>
</spring:bind>
</tr>

springmvc - partie3, ST universit dAngers

28/63

23.
</table>
24.
<table>
25.
<tr>
26.
<td><input type="submit" name="_target1" value="Suivant"></td>
27.
<td><input type="submit" name="_cancel" value="D&eacute;but"></td>
28.
</tr>
29.
</table>
30.
<input type="hidden" name="_page" value="0">
31.
</form>
32. </body>
33. </html>

lignes 16-21 : la saisie du nom est contrle par une balise <spring:bind>.
ligne 16 : le champ HTML de saisie du nom est associ au champ [nom] de l'objet de cl " formulaire ". On sait par
[spring-mvc-34-servlet.xml] (page 23) que cet attribut est associ un objet de type [Formulaire] qui a deux champs :
[nom, age].
ligne 18 : ${status.value} permet d'afficher la valeur actuelle de [formulaire.nom]
ligne 20 : ${status.errorMessage} permet d'afficher l'ventuel message d'erreur li la saisie du champ
[formulaire.nom]. Ce message sera construit par la mthode [validatePage] de l'assistant applique la page n 0
(attribut _page ligne 30).
ligne 26 : le bouton [Suivant] qui va provoquer la validation de la page n 0 (attribut _page ligne 30) et le passage la
page n 1 (attribut name= "_target1 ")
ligne 27 : le bouton [Dbut] qui va provoquer l'abandon de l'assistant (attribut name= "_cancel ") et l'excution de la
mthode [processCancel] de l'assistant.

Si le nom est valide, l'assistant va afficher la page n 1, c.a.d. la vue nomme " page2 " associe la page JSP [page2.jsp] :

Le code de [page2.jsp] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.

<%@
<%@
<%@
<%@

page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>


taglib uri="/WEB-INF/c.tld" prefix="c" %>
taglib uri="/WEB-INF/spring.tld" prefix="spring" %>
page isELIgnored="false" %>

ligne 16 : on affiche le nom saisi l'tape prcdente

<html>
<head>
<title>Spring(mvc-34)</title>
</head>
<body>
<h3>AssistantController (2/3) : saisie de l'&acirc;ge</h3>
<form method="post">
<table border="0">
<tr>
<td>Votre nom :</td>
<td>${formulaire.nom}</td>
</tr>
<tr>
<td>Votre &acirc;ge :</td>
<spring:bind path="formulaire.age">
<td>
<input type="text" name="${status.expression}" value="${status.value}">
</td>
<td>${status.errorMessage}</td>
</spring:bind>
</tr>
</table>
<input type="hidden" name="_page" value="1">
<table>
<tr>
<td><input type="submit" value="Pr&eacute;c&eacute;dent" name="_target0"></td>
<td><input type="submit" value="Suivant" name="_target2"></td>
<td><input type="submit" value="D&eacute;but" name="_cancel"></td>
</tr>
</table>
</form>
</body>
</html>

springmvc - partie3, ST universit dAngers

29/63

lignes 20-25 : la saisie de l'ge est contrle par une balise <spring:bind>.
ligne 20 : le champ HTML de saisie de l'ge est associ au champ [age] de l'objet de cl " formulaire ". Cet objet de
type [Formulaire] est le mme qui a servi la page n 0 (sessionForm=true dans la configuration de l'assistant).
ligne 22 : ${status.value} permet d'afficher la valeur actuelle de [formulaire.age]
ligne 24 : ${status.errorMessage} permet d'afficher l'ventuel message d'erreur li la saisie du champ
[formulaire.age]. Ce message sera construit par la mthode [validatePage] de l'assistant applique la page n 1
(attribut _page ligne 28).
ligne 31 : le bouton [Prcdent] qui va provoquer le retour la page n 0 (attribut name= "_target0 ") sans validation
des saisies de la page courante n 1 (allowDirtyForward=true dans la configuration de l'assistant).
ligne 32 : le bouton [Suivant] qui va provoquer la validation de la page n 1 (attribut _page ligne 28) et le passage la
page n 2 (attribut name= "_target2 ")
ligne 33 : le bouton [Dbut] qui va provoquer l'abandon de l'assistant (attribut name= "_cancel ") et l'excution de la
mthode [processCancel] de celui-ci.

Si l'ge est valide, l'assistant va afficher la page n 2, c.a.d. la vue nomme " page3 " associe la page JSP [page3.jsp] :

Le code de [page3.jsp] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.

<%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>


<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<%@ page isELIgnored="false" %>

ligne 13 : confirme le nom saisi


ligne 18 : confirme l'ge saisi
ligne 25 : le bouton [Prcdent] qui va provoquer le retour la page n 1 (attribut name= "_target1 ").
ligne 27 : le bouton [Suivant] qui va provoquer la validation de l'ensemble des pages de l'assistant (attribut
name= "_finish ") puis l'excution de la mthode [processFinish] de celui-ci.
ligne 26 : le bouton [Dbut] qui va provoquer l'abandon de l'assistant (attribut name= "_cancel ") et l'excution de la
mthode [processCancel] de celui-ci.

<html>
<head>
<title>Spring(mvc-34)</title>
</head>
<body>
<h3>AssistantController (3/3) : validation des saisies</h3>
<table border="0">
<tr>
<td>Votre nom :</td>
<td>${formulaire.nom}</td>
</tr>
<tr>
<td>Votre &acirc;ge :</td>
<td>${formulaire.age}</td>
</tr>
</table>
<!-- form -->
<form method="post">
<table>
<tr>
<td><input type="submit" value="Pr&eacute;c&eacute;dent" name="_target1"></td>
<td><input type="submit" value="D&eacute;but" name="_cancel"></td>
<td><input type="submit" value="Valider" name="_finish"></td>
</tr>
</table>
</form>
</body>
</html>

Dans notre cas, il n'y aura pas de surprise. L'ensemble des pages sera valid puisque :

on passe d'une page l'autre seulement si la page est valide (allowDirtyForward=false dans la configuration de
l'assistant)

la dernire page ne comporte pas de saisies


La mthode [processFinish] de l'assistant envoie la page de confirmation suivante :
springmvc - partie3, ST universit dAngers

30/63

Cette page a " validation " comme nom de vue et est associe la page [validation.jsp] suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.

<%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>


<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<%@ page isELIgnored="false" %>

ligne 10 : on affiche les saisies de l'objet associ la cl " formulaire ". Il faut faite attention ici, que cette page ne fait
pas partie des pages de l'assistant qui est dsormais termin. L'assistant maintenait dans la session un conteneur
[Formulaire] qu'il rendait accessible aux pages JSP via la cl " formulaire ". L'assistant tant termin, le conteneur
[Formulaire] n'existe plus. Il faudra donc que la mthode [processFinish] mette elle-mme le conteneur [Formulaire]
dans le modle de la vue " validation " afin que celle-ci y ait accs.

2.4.4

<html>
<head>
<title>Spring(mvc-34)</title>
</head>
<body>
Vos saisies [${formulaire.nom},${formulaire.age}] ont &eacute;t&eacute; enregistr&eacute;es.
<br><br>
A bient&ocirc;t.
<br><br>
<a href="index.htm">Recommencer</a>
</body>
</html>

Les classes du projet

Revenons au projet Eclipse / Tomcat pour tudier ses classes :

[Formulaire.java] est le conteneur des saisies de l'assistant


[AssistantController.java] est l'assistant

La classe [Formulaire.java] a t dcrite au paragraphe 2.4.2, page 27.


Le contrleur [AssistantController.java] est le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.

package istia.st.springmvc.exemples.web;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import
import
import
import

org.springframework.validation.BindException;
org.springframework.validation.Errors;
org.springframework.web.servlet.ModelAndView;
org.springframework.web.servlet.mvc.AbstractWizardFormController;

public class AssistantController extends AbstractWizardFormController {


protected ModelAndView processFinish(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
// confirmation des saisies
Map modle=new HashMap();
modle.put("formulaire",command);
return new ModelAndView("validation",modle);

springmvc - partie3, ST universit dAngers

31/63

23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.

}
protected ModelAndView processCancel(HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException errors)
throws Exception{
// retour la case dpart
return new ModelAndView("index");
}
// validation d'une page
protected void validatePage(Object formulaire, Errors errors, int page) {
// on rcupre le formulaire
Formulaire form=(Formulaire)formulaire;
// quelle page ?
switch (page) {
case 0:
validatePage0(form, errors);
break;
case 1:
validatePage1(form, errors);
break;
}
}
// validation page 0
protected void validatePage0(Formulaire form, Errors errors) {
// le nom doit tre non vide
String nom=form.getNom();
if(nom==null || nom.trim().length()==0){
errors.rejectValue("nom","formulaire.nom.obligatoire");
}
}

// validation page 1
protected void validatePage1(Formulaire form, Errors errors) {
// l'ge doit tre positif
int age=form.getAge();
if(age<1 || age>150){
errors.rejectValue("age","formulaire.age.invalide", new String[]{"1","150"},"Donne
invalide");
64.
}
65. }
66. }

ligne 14 : [ AssistantController ] drive de [AbstractWizardFormController]


lignes 35-47 : la mthode [validatePage] qui sert valider les donnes postes par la page n [page] de l'assistant. Ce
cas se produit lorsque l'utilisateur clique le bouton [Suivant] des pages [page1, page2] donc pour les pages n 0 et 1.
lignes 40-42 : si page=0, alors la mthode [validatePage] appelle la mthode [validatePage0] qui vrifie que le nom
n'est pas vide.
lignes 43-45 : si page=1, alors la mthode [validatePage] appelle la mthode [validatePage1] qui vrifie que l'ge est
dans l'intervalle [1,150]. Il faut rappeler que l'ge a t saisi dans la page n 1 tout d'abord comme chane de
caractres. Spring MVC a essay de transformer celle-ci en nombre entier pour la mettre dans le champ [age] de type
[int] de l'instance [Formulaire] qui sert de conteneur aux saisies de l'assistant. Si cette transformation choue, Spring
MVC

n'appelle pas la mthode [validatePage] de l'assistant

construit un objet [Errors] avec dedans une erreur associe la cl "typeMismatch"

provoque le raffichage de la page n 1


Les codes d'erreurs utiliss par les diffrentes mthodes de validation sont dfinis dans le fichier
[messages.properties] du projet :

Ce fichier a t dfini comme fichier des messages dans le fichier de configuration [applicationContext.xml] (cf page
23). Son contenu est le suivant :
1.
2.
3.

typeMismatch=Donne incorrecte !
formulaire.nom.obligatoire=Le nom est obligatoire ...
formulaire.age.invalide=Indiquez un ge dans l''intervalle [{0},{1}] ...

springmvc - partie3, ST universit dAngers

32/63

On notera ligne 3, l'utilisation de paramtres positionnels dans le message. Ces paramtres sont initialiss ligne 63 de
l'assistant avec respectivement les chanes 1 et 150.

lignes 25-32 : la mthode [processCancel] qui sert traiter le cas o l'utilisateur a demand l'abandon de l'assistant.
Dans ce cas, on demande l'affichage de la vue nomme " index " (ligne 31). Dans [vues.properties] (page 25), ce nom
est associe la page HTML " index.htm " dj prsente. C'est la page qui prsente le lien de dmarrage de
l'assistant.

lignes 16-24 : la mthode [processFinish] qui sert traiter le cas o l'utilisateur a demand la validation de l'ensemble
des pages de l'assistant.

on veut afficher la vue nomme " validation " associe la page JSP [validation.jsp] prsente page 31. Nous
avons alors expliqu que la mthode [processFinish] devait mettre le conteneur de saisies de type [Formulaire]
dans le modle de la vue " validation " associ la cl " formulaire ". C'est ce qui est fait lignes 20-21.

2.4.5

Conclusion

La prsentation du contrleur [AbstractWizardFormController] a t complexe. Heureusement, ce contrleur est, dans la


pratique, plus facile utiliser qu' expliquer. Le lecteur est invit tester l'application en demandant l'url
[http://localhost:8080/spring-mvc-34/] :

Lorsque diffrents tests auront t faits, il est conseill de modifier dans (spring-mvc-34-servlet.xml) les attributs
[allowDirtyBack] et [allowDirtyForward] de l'assistant afin de voir l'influence de ces deux attributs sur son comportement :
1.<!-- le contrleur de l'application-->
2. <bean id="AssistantController"
3.
class="istia.st.springmvc.exemples.web.AssistantController">
4. ...
5.
<property name="allowDirtyBack">
6.
<value>true</value>
7.
</property>
8.
<property name="allowDirtyForward">
9.
<value>true</value>
10. </property>
11. </bean>

3 Produire des vues Excel ou PDF


3.1

Introduction

Revenons sur l'architecture d'une application Spring MVC :


Couche Interface Utilisateur[ui]
utilisateur

DispatcherServlet
1

Couche mtier
[metier]

2
3

Controller
4

View

Couche d'accs
aux donnes
[dao]

Donnes

Modle Map
6

springmvc - partie3, ST universit dAngers

33/63

On sait que l'opration (7) de rendu d'une vue est faite par une classe implmentant l'interface [View] de Spring :

Cette interface n'a qu'une unique mthode charge d'envoyer une rponse au client. La forme exacte de celle-ci dpend de la
classe d'implmentation utilise. Parmi les classes d'implmentation prdfinies de Spring et prsentes ci-dessus, nous en
avons rencontr deux :

JstlView : la classe charge de rendre des pages JSP / JSTL

RedirectView : une classe qui envoie au client une demande de redirection


Nous allons prsenter dans cet exemple deux nouvelles implmentations : AbstractExcelView et AbstractPdfView, deux
classes abstraites qui une fois drives, permettent de gnrer respectivement un flux Excel ou un flux PDF comme rponse au
client.
Pour illustrer l'utilisation de ces deux classes, nous allons reprendre l'assistant que nous venons d'tudier en changeant
simplement sa dernire page. Celle-ci devient la suivante :

L'utilisateur peut choisir trois formats de confirmation de ses saisies.


Le choix [Html] correspond la page de confirmation de l'assistant (spring-mvc-34) :

Le choix [Excel] envoie au client une confirmation des saisies sous la forme d'un flux Excel :

springmvc - partie3, ST universit dAngers

34/63

Le choix [Pdf] envoie au client une confirmation des saisies sous la forme d'un flux PDF :

Le nouveau projet Eclipse / Tomcat s'appelle [spring-mvc-35]. Son organisation est la suivante :

springmvc - partie3, ST universit dAngers

35/63

Le projet [spring-mvc-35] est, pour une large part, identique au projet [spring-mvc-34]. On a le mme assistant. Seule sa
dernire page [page3.jsp] change :

Le code JSP de la page est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.

<%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>


<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Spring(mvc-35)</title>
</head>
<body>
<h3>AssistantController (3/3) : validation des saisies</h3>
<table border="0">
<tr>
<td>Votre nom :</td>
<td>${formulaire.nom}</td>
</tr>
<tr>
<td>Votre &acirc;ge :</td>
<td>${formulaire.age}</td>
</tr>
</table>
<!-- forms -->
<form method="post">

springmvc - partie3, ST universit dAngers

36/63

23.
<table>
24.
<tr>
25.
<td>Type de document d&eacute;sir&eacute;</td>
26.
<td>
27.
<input type="radio" value="excel" name="doc">Excel
28.
<input type="radio" value="pdf" name="doc">Pdf
29.
<input type="radio" value="html" name="doc" checked>Html
30.
</td>
31.
</tr>
32.
<tr>
33.
<td><input type="submit" value="Pr&eacute;c&eacute;dent" name="_target1"></td>
34.
<td><input type="submit" value="D&eacute;but" name="_cancel"></td>
35.
<td><input type="submit" value="Valider" name="_finish"></td>
36.
</tr>
37.
</table>
38.
</form>
39. </body>
40. </html>

la diffrence avec la page [page3.jsp] du projet prcdent se trouve lignes 27-30. La valeur d'un groupe de boutons
radio nomm " doc " va tre poste. Les trois valeurs possibles de ce groupe de boutons sont " excel " (ligne 27),
" pdf " (ligne 28) et " html " (ligne 29).

Le traitement des valeurs postes par les boutons [Prcdent] et [Dbut] est le mme que prcdemment. Celui du POST
provoqu par le bouton [Valider] (ligne 35) doit tre modifi. En effet, il nous faut rcuprer la valeur poste pour le groupe de
boutons radio nomm " doc ". Parce que le nom du bouton [Valider] est " _finish ", nous savons que le POST est trait par la
mthode [processFinish] de lassistant. Cette mthode devient la suivante :
1. protected ModelAndView processFinish(HttpServletRequest request,
2.
HttpServletResponse response, Object command, BindException errors)
3.
throws Exception {
4.
// confirmation des saisies
5.
Map modle = new HashMap();
6.
modle.put("formulaire", command);
7.
// type de confirmation souhaite
8.
String vue = RequestUtils.getStringParameter(request, "doc", "html");
9.
// on rend le [ModelAndView]
10. return new ModelAndView(vue, modle);
11.}

lignes 5-6 : le conteneur des saisies, de type [Formulaire] est mis dans le modle de la vue afficher, associ la cl
" formulaire ".
ligne 8 : on rcupre la valeur poste pour le groupe de boutons radio nomm " doc ". Nous aurions pu la rcuprer en
crivant simplement :
String vue=(String)request.getParameter(" doc ");

Ici, nous avons utilis la classe utilitaire [RequestUtils] :

Cette classe offre un certain nombre de mthodes statiques facilitant lextraction dinformations de la chane de
paramtres de la requte : getIntParameter, getLongParameter, getDoubleParameter, getStringParameter, ... Ces
mthodes offrent plusieurs versions surcharges. Lune delles permet de donner une valeur par dfaut au paramtre
demand si celui-ci est absent de la requte. Ici linstruction :
String vue = RequestUtils.getStringParameter(request, "doc", "html");

donne [vue] la valeur du paramtre " doc " ou dfaut la valeur " html " si le paramtre " doc " est absent de la
requte. Ce dernier cas nest pas possible dans le fonctionnement normal de notre exemple. Au final, la variable [vue]
a donc lune des valeurs [" html ", "excel ", "pdf "].
On notera que puisque le groupe de boutons radio nomm " doc " est une saisie, on aurait pu ajouter au conteneur des
saisies [Formulaire] un nouveau champ :
String doc;

et alors on aurait crit :


springmvc - partie3, ST universit dAngers

37/63

String vue=((Formulaire)command).doc;

Nous avons voulu montrer quon pouvait parfois utiliser directement lobjet [request] pour y rcuprer des paramtres
posts.

ligne 10 : la variable [vue] est utilis comme nom de vue du [ModelAndView] rendu par la mthode [processFinish].

Le contrleur gnral [DispatcherServlet] va demander la classe charge de rendre la vue de sexcuter. Cette classe est
trouve grce au rsolveur de noms de vues, ici [ResourceBundleViewResolver]. Cest donc le fichier [vues.properties] qui va
tre utilis. Le contenu de celui-ci diffre de ce quil tait dans le projet prcdent :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

#page1
page1.class=org.springframework.web.servlet.view.JstlView
page1.url=/WEB-INF/vues/page1.jsp
#page2
page2.class=org.springframework.web.servlet.view.JstlView
page2.url=/WEB-INF/vues/page2.jsp
#page3
page3.class=org.springframework.web.servlet.view.JstlView
page3.url=/WEB-INF/vues/page3.jsp
#index
index.class=org.springframework.web.servlet.view.JstlView
index.url=/index.htm
#excel
excel.class=istia.st.springmvc.exemples.web.ExcelView
#pdf
pdf.class=istia.st.springmvc.exemples.web.PdfView
#html
html.class=org.springframework.web.servlet.view.JstlView
html.url=/WEB-INF/vues/html.jsp

lignes 1-12 : les vues " page1 ", " page2 ", " page3 " et " index " restent inchanges
lignes 17-19 : la vue " html " nest rien dautre que la vue " validation " du projet prcdent.
ligne 14 : dfinit la classe de rendu vue " excel ". Cette classe [ExcelView] mettra le modle de la vue dans un flux
Excel.
ligne 16 : dfinit la classe de rendu vue "pdf". Cette classe [PdfView] mettra le modle de la vue dans un flux PDF.

3.2

La vue HTML

Le fichier [vues.properties] indique que la vue nomme " html " est la page JSP [html.jsp]. Cette page nest autre que la page
[validation.jsp] du projet prcdent qui a t renomme.

3.3

La vue Excel

Le fichier [vues.properties] indique que la vue nomme "excel" doit tre rendue par un objet de type [ExcelView]. Cette classe,
que nous allons construire, drive de la classe prdfinie [AbstractExcelView] :

On voit que la classe [AbstractExcelView] implmente linterface [View], condition que doit remplir toute classe charge de
rendre une vue. On sait que cette interface na quune mthode :

springmvc - partie3, ST universit dAngers

38/63

La mthode [render] de la classe [AbstractExcelView] va :


1. construire un flux Excel et mettre le modle [model] dedans
2. encapsuler ce flux Excel dans un flux HTTP
3. envoyer le flux HTTP au client grce lobjet [response]
La classe [AbstractExcelView] assure elle-mme les tapes 2 et 3. Ltape 1 est assure par la mthode abstraite
[buildExcelDocument] de la classe [AbstractExcelView] :

On y retrouve les trois paramtres de la mthode [render]. Le paramtre [HSSFWorkbook workbook] reprsente un classeur
Excel vide. Le modle [model] doit tre insr dans celui-ci. La classe gnrique [AbstractExcelView] ne peut savoir comment
car cela dpend bien videmment de chaque application. La classe [buildExcelDocument] est donc dclare abstraite et doit
tre redfinie dans une classe drive. Celle-ci na que cette mthode redfinir.
Le type [ HSSFWorkbook ] nest pas un type Spring. Spring sappuie ici sur un projet Apache : Jakarta POI disponible
lurl [http://jakarta.apache.org/poi/] :

Les classes de ce projet sont disponibles dans une archive que nous devons inclure dans le ClassPath du projet [spring-mvc-35]
:

La classe drive de [AbstractExcelView] charge dafficher la vue nomm " excel " est de type [ExcelView] comme indiqu
dans le fichier [vues.properties] :
1.
2.

#excel
excel.class=istia.st.springmvc.exemples.web.ExcelView

Cette classe est dfinie dans [WEB-INF/src] :

springmvc - partie3, ST universit dAngers

39/63

Son code est le suivant :


1. package istia.st.springmvc.exemples.web;
2.
3. import java.util.Map;
4.
5. import javax.servlet.http.HttpServletRequest;
6. import javax.servlet.http.HttpServletResponse;
7.
8. import org.apache.poi.hssf.usermodel.HSSFCell;
9. import org.apache.poi.hssf.usermodel.HSSFCellStyle;
10. import org.apache.poi.hssf.usermodel.HSSFFont;
11. import org.apache.poi.hssf.usermodel.HSSFSheet;
12. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
13. import org.apache.poi.hssf.util.HSSFColor;
14. import org.apache.poi.hssf.util.Region;
15. import org.springframework.web.servlet.view.document.AbstractExcelView;
16.
17. public class ExcelView extends AbstractExcelView {
18.
19. protected void buildExcelDocument(Map modle, HSSFWorkbook classeur,
20.
HttpServletRequest request, HttpServletResponse response)
21.
throws Exception {
22.
// on rcupre le formulaire dans le modle
23.
Formulaire form = (Formulaire) modle.get("formulaire");
24.
// on cre une feuille dans le classeur
25.
HSSFSheet feuille = classeur.createSheet("confirmation");
26.
// les polices de caractres
27.
// font 0
28.
HSSFFont font0 = classeur.createFont();
29.
font0.setFontHeightInPoints((short) 24);
30.
font0.setFontName("Garamond");
31.
font0.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
32.
// font 1
33.
HSSFFont font1 = classeur.createFont();
34.
font1.setFontHeightInPoints((short) 12);
35.
font1.setFontName("Garamond");
36.
font1.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
37.
// font 2
38.
HSSFFont font2 = classeur.createFont();
39.
font2.setFontHeightInPoints((short) 12);
40.
font2.setFontName("Garamond");
41.
font2.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
42.
43.
// les styles utiliss
44.
// style 0
45.
HSSFCellStyle style0 = classeur.createCellStyle();
46.
style0.setAlignment(HSSFCellStyle.ALIGN_CENTER);
47.
style0.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
48.
style0.setFillForegroundColor(HSSFColor.LIGHT_GREEN.index);
49.
style0.setFont(font0);
50.
// style 1
51.
HSSFCellStyle style1 = classeur.createCellStyle();
52.
style1.setAlignment(HSSFCellStyle.ALIGN_CENTER);
53.
style1.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
54.
style1.setFillForegroundColor(HSSFColor.LIGHT_ORANGE.index);
55.
style1.setFont(font1);
56.
// style 2
57.
HSSFCellStyle style2 = classeur.createCellStyle();
58.
style2.setAlignment(HSSFCellStyle.ALIGN_CENTER);
59.
style2.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
60.
style2.setFillForegroundColor(HSSFColor.LIGHT_BLUE.index);
61.
style2.setFont(font2);
62.
63.
// contenu de la feuille
64.
// ligne 0
65.
int ligne = 0;
66.
HSSFCell cell = getCell(feuille, ligne, 0);
67.
cell.setCellValue("Confirmation de vos saisies");
68.
// cellule 0 tendue jusqu' la cellule 7
69.
feuille.addMergedRegion(new Region(0, (short) 0, 0, (short) 7));
70.
// style de la cellule 0
71.
cell.setCellStyle(style0);
springmvc - partie3, ST universit dAngers

40/63

72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90. }
91. }

// ligne 2
ligne += 2;
cell = getCell(feuille, ligne, 0);
cell.setCellValue("Nom");
cell.setCellStyle(style1);
cell = getCell(feuille, ligne, 1);
cell.setCellValue("Age");
cell.setCellStyle(style1);
// ligne 3
ligne++;
cell = getCell(feuille, ligne, 0);
cell.setCellValue(form.getNom());
cell.setCellStyle(style2);
cell = getCell(feuille, ligne, 1);
cell.setCellValue(form.getAge());
cell.setCellStyle(style2);

ligne 17 : la classe [ExcelView] drive de la classe [AbstractExcelView] de Spring


lignes 19-21 : la mthode abstraite [buildExcelDocument] de [AbstractExcelView] est redfinie

Rappelons que la mthode [buildExcelDocument] reoit un classeur Excel vide quelle doit remplir avec le modle. Ce classeur
est le paramtre [HSSFWorkbook classeur] de la mthode. Pour construire le classeur Excel, il faut consulter la documentation
du projet Jakarta POI. Un bon point de dpart est lurl [http://jakarta.apache.org/poi/hssf/quick-guide.html] (avril 2006) :

Nous commentons les grandes lignes de la mthode [buildExcelDocument] qui vise construire le classeur Excel suivant :

ligne 23 : rcupre le modle rendre. Nous savons que cest une instance de la classe [Formulaire].
ligne 25 : cre une feuille dans le classeur Excel et lui donne le nom " confirmation "
lignes 28-31 : dfinit un type de caractres " font0 "
ligne 28 : linstance " font0 " est cre
ligne 29 : fixe la taille de la police 24 points

springmvc - partie3, ST universit dAngers

41/63

ligne 30 : fixe " Garamond " la police utilise


ligne 31 : fixe le style des caractres " gras "
ligne 33-36 : une police " font1 " (12 pts, Garamond, " gras ") est dfinie
ligne 38-41 : une police " font2 " (12 pts, Garamond, " normal ") est dfinie
lignes 45-49 : dfinissent un style " style0 " de cellule
ligne 45 : le style " style0 " est cr
ligne 46 : fixe lalignement horizontal du style " centr "
ligne 47 : fixe le motif de remplissage des cellules du style " solid ", c.a.d. un motif plein couvrant la totalit de la
cellule
ligne 48 : fixe la couleur de remplissage du style " vert clair "
ligne 49 : fixe la police de caractres du style " font0 ", le style dfini lignes 28-31
lignes 51-55 : un style " style1 " est cr
lignes 57-61 : un style " style2 " est cr
ligne 66 : cre une rfrence sur la cellule (0,0) de la feuille courante
ligne 67 : met le texte " Confirmation de vos saisies " dans cette cellule
ligne 69 : cre une rgion, c.a.d. une zone rectangulaire de cellules. Celle-ci peut tre dfinie par les coordonnes des
cellules de dbut et de fin dune des diagonales du rectangles. Ici Region(0,0,0,7) dfinit la rgion allant de la cellule
(0,0) la cellule (0,7), donc une rgion de 8 cellules dans le coin suprieur gauche de la feuille. Les cellules de cette
rgion sont fusionnes (feuille.addMergedRegion). Ceci a pour consquence que la cellule (0,0) occupe dsormais
toute la rgion cre.
ligne 71 : le style " style0 " est appliqu la cellule (0,0)
lignes 75-80 : le texte " Nom " est mis dans la cellule (2,0), le texte " Age " dans la cellule (2,1). Le style " style1 " est
appliqu aux deux cellules.
lignes 84-89 : la valeur du champ nom du formulaire est mis dans la cellule (3,0), la valeur du champ age dans la
cellule (3,1). Le style " style2 " est appliqu aux deux cellules.

Cest tout. Le modle [model] a t mis dans un classeur Excel. La mthode [render] de [AbstractExcelView] va se charger
dencapsuler celui-ci dans la rponse HTTP envoye au client.
Aux tests, cela donne les choses suivantes :

Si on regarde le flux HTTP envoy au client, avec un outil tel que LiveHTTPHeaders, on obtient la chose suivante :
1.
2.
3.
4.

HTTP/1.x 200 OK
Server: Apache-Coyote/1.1
Pragma: No-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT

springmvc - partie3, ST universit dAngers

42/63

5.
6.
7.
8.
9.

Cache-Control: no-cache, no-store


Content-Type: application/vnd.ms-excel;charset=ISO-8859-1
Content-Language: fr-FR
Transfer-Encoding: chunked
Date: Sat, 15 Apr 2006 14:03:25 GMT

la ligne 6 indique quaprs les enttes HTTP (lignes 1-9), le serveur va envoyer un document de type MIME
[application/vnd.ms-excel]. A rception de cet entte, le navigateur sait quel type de document il va recevoir.
la ligne 8 indique que le document va tre envoy par morceaux. On ne les voit pas ici. Pour chacun deux, le serveur
indique dabord combien doctets il va envoyer puis il envoie ceux-ci.
lorsque le navigateur a reu lintgralit du document, il peut soit le copier sur le disque soit proposer sa visualisation
sil existe sur la machine de rception un programme capable de visualiser le type de document reu. Cest pourquoi
ci-dessus, le navigateur Firefox a propos soit la visualisation du document par Excel qui est prsent sur la machine
cliente, soit la sauvegarde du document sur le disque.

Le lecteur pourra vrifier que le document Excel reu est bien celui construit par la mthode [buildExcelDocument] que nous
avons commente.

3.4

La vue PDF

Le fichier [vues.properties] (cf page 38) indique que la vue nomme "pdf" doit tre rendue par un objet de type [PdfView].
Cette classe, que nous allons construire, drive de la classe prdfinie [AbstractPdfView] :

On voit que la classe [AbstractPdfView] implmente linterface [View], condition que doit remplir toute classe charge de
rendre une vue. On sait que cette interface na quune mthode :

La mthode [render] de la classe [AbstractPdfView] va :


1. construire un flux PDF et mettre le modle [model] dedans
2. encapsuler ce flux PDF dans un flux HTTP
3. envoyer le flux HTTP au client grce lobjet [response]
La classe [AbstractPdfView] assure elle-mme les tapes 2 et 3. Ltape 1 est assure par la mthode abstraite
[buildPdfDocument] de la classe [AbstractPdfView] :

On y retrouve les trois paramtres de la mthode [render]. Le paramtre [com.lowagie.text.Document document] reprsente un
document PDF vide. Le modle [model] doit tre insr dans celui-ci. La classe gnrique [AbstractPdfView] ne peut savoir
comment, car cela dpend bien videmment de chaque application. La classe [buildPdfDocument] est donc dclare abstraite et
doit tre redfinie dans une classe drive. Celle-ci na que cette mthode redfinir.
Le type [com.lowagie.text.Document] nest pas un type Spring. Spring sappuie ici sur un projet tiers appel iText et disponible
lurl [http://www.lowagie.com/iText/] :

springmvc - partie3, ST universit dAngers

43/63

Les classes de ce projet sont disponibles dans une archive que nous devons inclure dans le ClassPath du projet [spring-mvc-35]
:

La classe drive de [AbstractPdfView] charge dafficher la vue nomm "pdf" est de type [PdfView] comme indiqu dans le
fichier [vues.properties] (cf page 38):
1.
2.

#pdf
pdf.class=istia.st.springmvc.exemples.web.PdfView

Cette classe est dfinie dans [WEB-INF/src] :

Son code est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

package istia.st.springmvc.exemples.web;
import java.awt.Color;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.view.document.AbstractPdfView;
import
import
import
import
import
import

com.lowagie.text.Document;
com.lowagie.text.Element;
com.lowagie.text.Paragraph;
com.lowagie.text.pdf.PdfPCell;
com.lowagie.text.pdf.PdfPTable;
com.lowagie.text.pdf.PdfWriter;

public class PdfView extends AbstractPdfView {

protected void buildPdfDocument(Map modle, Document doc, PdfWriter writer, HttpServletRequest


request, HttpServletResponse response) throws Exception {
21.
// on rcupre le formulaire dans le modle
22.
Formulaire form=(Formulaire)modle.get("formulaire");
23.
// une table 2 colonnes
springmvc - partie3, ST universit dAngers
44/63

24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56. }

PdfPTable table = new PdfPTable(2);


// une cellule
PdfPCell cell = new PdfPCell(new Paragraph("Confirmation de vos saisies"));
// qui prendra deux colonnes
cell.setColspan(2);
// et sera centr
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
// ajout de la cellule la table
table.addCell(cell);
// 1re ligne, 1re colonne
cell=new PdfPCell(new Paragraph("Nom"));
cell.setBackgroundColor(new Color(0xC0, 0xC0, 0xC0));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
// 1re ligne, 2ime colonne
cell=new PdfPCell(new Paragraph("Age"));
cell.setBackgroundColor(new Color(0xC0, 0xC0, 0xC0));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
// 2ime ligne, 1re colonne
cell=new PdfPCell(new Paragraph(form.getNom()));
cell.setBackgroundColor(new Color(0xA0, 0xB0, 0xC0));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
// 2ime ligne, 2ime colonne
cell=new PdfPCell(new Paragraph(""+form.getAge()));
cell.setBackgroundColor(new Color(0xA0, 0xB0, 0xC0));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
// ajout de la table au document
doc.add(table);
}

ligne 18 : la classe [PdfView] drive de la classe [AbstractPdfView] de Spring


ligne 20 : la mthode abstraite [buildPdfDocument] de [AbstractPdfView] est redfinie

Rappelons que la mthode [buildPdfDocument] reoit un document PDF vide quelle doit remplir avec le modle. Ce
document est le paramtre [Document doc] de la mthode. Pour construire le document PDF, il faut consulter la documentation
du projet iText. Un bon point de dpart est lurl [http://itextdocs.lowagie.com/tutorial/] (avril 2006) :

Nous commentons les grandes lignes de la mthode [buildPdfDocument] qui vise construire le document PDF suivant :

ligne 22 : rcupre le modle rendre. Nous savons que cest une instance de la classe [Formulaire].
ligne 24 : cre la table deux colonnes qui va contenir le modle. On ne prcise pas le nombre de lignes de la table.
La table va tre remplie de cellules. Lordre de remplissage par dfaut est un remplissage ligne par ligne, de la droite
vers la gauche. Une cellule est ajoute au premier emplacement libre de la ligne courante. Si celle-ci est pleine, une
nouvelle ligne est cre et la cellule est place dans la premire colonne de cette ligne.
ligne 26 : cre une cellule PDF et met dedans un texte sous la forme dun paragraphe.

springmvc - partie3, ST universit dAngers

45/63

ligne 28 : la cellule prcdente va staler sur deux colonnes


ligne 30 : le texte de la cellule sera centr horizontalement
ligne 32 : la cellule cre ligne 26 est ajoute la table. Elle en forme la premire ligne.
lignes 34-37 : la cellule (1,0) de la table est dfinie contient le texte "Nom"
lignes 39-42 : la cellule (1,1) de la table est dfinie contient le texte "Age"
lignes 44-47 : la cellule (2,0) de la table est dfinie contient la valeur du champ [formulaire.nom]
lignes 49-52 : la cellule (2,1) de la table est dfinie contient la valeur du champ [formulaire.age]
ligne 54 : la table cre ligne 24 est ajoute au document PDF

Cest tout. Le modle [model] a t mis dans un document PDF. La mthode [render] de [AbstractPdfView] va se charger
dencapsuler celui-ci dans la rponse HTTP envoye au client.
Aux tests, cela donne les choses suivantes :

Si on regarde le flux HTTP envoy au client, avec un outil tel que LiveHTTPHeaders, on obtient la chose suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.

HTTP/1.x 200 OK
Server: Apache-Coyote/1.1
Pragma: No-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Cache-Control: no-cache, no-store
Content-Type: application/pdf;charset=ISO-8859-1
Content-Language: fr-FR
Content-Length: 1162
Date: Mon, 17 Apr 2006 12:57:08 GMT

la ligne 6 indique quaprs les enttes HTTP (lignes 1-9), le serveur va envoyer un document de type MIME
[application/pdf]. A rception de cet entte, le navigateur sait quel type de document il va recevoir.
la ligne 8 indique que le document PDF comporte 1162 octets.
lorsque le navigateur a reu lintgralit du document, il peut soit le copier sur le disque soit proposer sa visualisation
sil existe sur la machine de rception un programme capable de visualiser le type de document reu. Pour les
documents PDF, les navigateurs rcents ont la plupart du temps un plugin permettant de visualiser le document PDF
lintrieur du navigateur.

Le lecteur pourra vrifier que le document PDF reu est bien celui construit par la mthode [buildPdfDocument] que nous
avons commente.

4 Tlchargement de documents vers le serveur


4.1

Introduction

Spring offre des outils pour faciliter le tlchargement de documents du client vers le serveur. Pour les prsenter, nous allons
tudier lapplication suivante :

3
2

4
springmvc - partie3, ST universit dAngers

46/63

La page principale de lapplication est un formulaire destin dsigner le document local (c.a.d. un document prsent sur le
disque du client) tlcharger sur le serveur. Les lments HTML du formulaire ci-dessus sont les suivants :
n
1

type HTML

nom

rle

<input type= "text ">

nom

2-3

<input type= "file ">

contenu

<input type= "submit "> action

provoque le tlchargement sur le serveur du document prcis en [2] avec


lidentifiant prcis en [1].

<input type= "submit "> action

idem que prcdemment mais ct serveur, la mthode utilise pour enregistrer le


document sera diffrente.

donner un nom au document qui va tre tlcharg sur le serveur


lensemble bote de saisie bouton permettant de dsigner le document
tlcharger. Son chemin peut tre indiqu soit en :

le tapant dans [2]

le dsignant avec le bouton [3]

Le projet Eclipse / Tomcat sappellera (spring-mvc-35) et sera le suivant :

Le dossier [uploads] sera le dossier o seront placs les documents tlchargs sur le serveur. Le formulaire de lapplication
prsent page 47 peut tre trait par un formulaire de type [MultiActionController] puisquil comporte deux boutons de type
[Submit]. On peut aussi le traiter avec un simple contrleur de type [SimpleFormController] et cest ce que nous faisons ici.
Le fichier de configuration [spring-mvc-36-servlet.xml] de lapplication est le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/springbeans.dtd">
<beans>
<!-- les mappings de l'application-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="upload.html">UploadController</prop>
</props>
</property>
</bean>
<!-- le rsolveur de vues -->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename">
<value>vues</value>
</property>

springmvc - partie3, ST universit dAngers

47/63

17. </bean>
18. <!-- le contrleur de l'application-->
19. <bean id="UploadController"
20.
class="istia.st.springmvc.exemples.web.UploadController">
21.
<property name="sessionForm">
22.
<value>true</value>
23.
</property>
24.
<property name="commandClass">
25.
<value>istia.st.springmvc.exemples.web.Document</value>
26.
</property>
27.
<property name="commandName">
28.
<value>document</value>
29.
</property>
30.
<property name="formView">
31.
<value>upload</value>
32.
</property>
33.
<property name="validator">
34.
<ref local="validateur"/>
35.
</property>
36. </bean>
37. <!-- validateur -->
38. <bean id="validateur" class="istia.st.springmvc.exemples.web.ValidateurDocument"/>
39. <!-- le gestionnaire d'exceptions -->
40. <bean
41.
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
42.
<property name="exceptionAttribute">
43.
<value>exception</value>
44.
</property>
45.
<property name="defaultStatusCode">
46.
<value>200</value>
47.
</property>
48.
<property name="defaultErrorView">
49.
<value>exception</value>
50.
</property>
51. </bean>
52. <!-- upload de fichiers -->
53. <bean id="multipartResolver"
54.
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
55.
<property name="maxUploadSize">
56.
<value>1000000</value>
57.
</property>
58. </bean>
59. <!-- le fichier des messages -->
60. <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
61.
<property name="basename">
62.
<value>messages</value>
63.
</property>
64. </bean>
65. </beans>

ligne 8 : lunique url traite par lapplication est [/upload.html]. Cette url est traite par un contrleur dfini lignes 1939.
lignes 19-39 : dfinissent lunique contrleur de lapplication
ligne 20 : le contrleur est de type [UploadController], un type propritaire driv du type Spring
[SimpleFormController]. Nous avons dj tudi ce type de contrleur dans larticle 2.
lignes 24-26 : dfinissent le conteneur des saisies du formulaire. Il est de type [Document] un type propritaire qui
stockera deux informations :

lidentifiant donn au document tlcharg (saisie [1])

le contenu du document tlcharg (saisie [2])


lignes 27-29 : la cl via laquelle sera accessible le document tlcharg dans la vue dfinie par la proprit
[formView] (lignes 30-32) qui est la vue du formulaire de saisies.
lignes 30-32 : le nom de la vue qui contient le formulaire de saisies. Ici la vue sappelle " upload ".
lignes 33-35 : le validateur charg de vrifier la validit des donnes postes
lignes 21-23 : la mise en session ou non du conteneur de saisies entre son GET et son POST. Ici, il restera en session.

ligne 38 : le validateur rfrenc par le contrleur [UploadController]. Cest une classe propritaire que nous serons
amens crire.

lignes 40-51 : le gestionnaire des exceptions qui remonteront jusqu [DispatcherServlet]. Ici, nous indiquons que la
vue nomme " exception " devra tre affiche (ligne 43). On notera ligne 46, le code dtat de la rponse HTTP. Dans
les exemples de larticle 2, on mettait 500 comme code qui est le code " Internal server error " signalant que le serveur
a rencontr un problme. Si le navigateur [Firefox] accepte dafficher une page envoye avec le code 500, ce nest
pas le cas dInternet Explorer qui lui, affiche sa propre page derreur au lieu de celle envoye par le serveur. Donc
certains exemples de larticle 2 ne fonctionnent pas avec IE. Ici, le code 200 signifiant "OK", la page envoye par le
serveur sera correctement affiche par IE.

springmvc - partie3, ST universit dAngers

48/63

lignes 13-17 : le rsolveur de noms de vues, ici [ResourceBundleViewResolver]. Les vues seront dfinies dans un
fichier [vues.properties]. Ce sera le cas des vues nommes " upload " et " confirmation " dfinies par le contrleur
[UploadController] ainsi que de la vue " exception " dfinie par le gestionnaire dexceptions
[SimpleMappingExceptionResolver].

lignes 60-64 : le gestionnaire des messages, notamment des messages derreurs. Comme dhabitude, cest
[ResourceBundleMessageSource] et les messages seront placs dans un fichier [messages.properties] qui sera cherch
dans le Classpath de lapplication. Jusqu maintenant, cette ressource tait dfinie dans [applicationContext.xml].
Nous innovons en le plaant dans le fichier [spring-mvc-36-servlet.xml]. Quelle est la diffrence ? Dans une
architecture 3tier, les couches [mtier] et [dao] seront dfinies dans le fichier [applicationContext.xml]. Il se peut que
ces couches aient galement besoin dune rfrence sur le fichier des messages. Si ctait le cas, celui-ci devrait tre
dfini dans [applicationContext.xml].

Tout ce qui prcde a dj t rencontr au moins une fois dans lune ou lautre des applications tudies jusqu maintenant.
La seule nouveaut est dans les lignes 53-58 qui dfinissent lobjet charg de grer les tlchargements de fichiers du client
vers le serveur.
1.
2.
3.
4.
5.
6.
7.

<!-- upload de fichiers -->


<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize">
<value>1000000</value>
</property>
</bean>

Ces lignes dfinissent un intercepteur de type [MultipartResolver] qui va participer au traitement de la requte du client, si
celle-ci est de type " multipart ". Nous tudions maintenant ce type dintercepteur.

4.2

Les intercepteurs de type [MultipartResolver]

Le formulaire HTML dun tlchargement de fichiers vers le serveur a un type spcial :


<form method="post" enctype="multipart/form-data">

Lattribut [enctype] indique au serveur quil va recevoir un document en plusieurs parties. Dans ce document, il y aura :

les valeurs postes, ici les paramtres " nom ", " contenu " et " action "

le contenu du document tlcharg


Lorsque ce type de formulaire est post au serveur, la requte HTTP faite au serveur par le client a la forme suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.

POST /spring-mvc-36/upload.html HTTP/1.1


Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.2) Gecko/20060308
Firefox/1.5.0.2
Accept:
text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.
5
Accept-Language: fr-fr,fr;q=0.8,en;q=0.6,en-us;q=0.4,de;q=0.2
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/spring-mvc-36/upload.html
Cookie: JSESSIONID=4950824B472EEEB6B95751E537DB4D3F
Content-Type: multipart/form-data; boundary=---------------------------23281168279961
Content-Length: 1543
-----------------------------23281168279961
Content-Disposition: form-data; name="nom"
upload
-----------------------------23281168279961
Content-Disposition: form-data; name="contenu"; filename="upload.jsp"
Content-Type: application/octet-stream
....
-----------------------------23281168279961
Content-Disposition: form-data; name="action"
Enregistrer1
-----------------------------23281168279961--

springmvc - partie3, ST universit dAngers

49/63

ligne 12 : le serveur est averti quil va recevoir un document au format " multipart ". Ce format ce caractrise par des
envois de paquets dlimits par un marqueur. Celui-ci est la valeur de lattribut " boundary " de la ligne 12 quon
retrouve aux lignes 15, 19, 24 et 28.
lignes 16-18 : envoi du paramtre [nom] et de sa valeur [upload]
lignes 20-23 : envoi du flux doctets du document tlcharg.

ligne 20 : envoi du paramtre [contenu] et de sa valeur [upload.jsp]. Si le chemin du document est


[/elmt1/elmt2/.../elmntN], seule la dernire partie du chemin [elmntN] est envoye au serveur.

ligne 21 : indique au serveur quil va recevoir un flux doctets

lignes 22-23 : le flux doctets du document [upload.jsp]


lignes 25-27 : envoi du paramtre [action] et de sa valeur [Enregistrer1]

Lorsque le contrleur gnral [DispatcherServlet] reoit une requte HTTP avec lattribut [Content-type] suivant :
Content-Type: multipart/form-data; boundary=---------------------------23281168279961

il cherche si lapplication a dfini un bean did [multipartResolver]. Si oui, ce bean sera charg de traiter la requte de type
" multipart ". On peut voir le bean [multipartResolver] comme une classe intercepteur :
Couche Interface Utilisateur[ui]
utilisateur

DispatcherServlet

MultipartResolver
2

1
2

Controller

3
4

View

Mtier | DAO

Donnes

Modle Map
6

[DispatcherServlet] transmet la requte du client sous la forme dun objet [HttpServletRequest] lintercepteur
[MultipartResolver]
[MultipartResolver] transforme cet objet en un objet [MultipartHttpServletRequest] et le transmet lobjet
[Controller] charg de traiter la requte du client. Celui-ci, va alors avoir un accs simplifi au document tlcharg

Spring offre deux classes capables dimplmenter lintercepteur [MultipartResolver] ci-dessus :

Les classes [CommonsMultiPartResolver] et [CosMultiPartResolver] implmentent linterface [MultiPartResolver] suivante :

La mthode la plus importante est [resolveMultipart] qui partir de la requte multipart reue par [DispatcherServlet] va crer
une instance de type [MultipartHttpServletRequest] :

springmvc - partie3, ST universit dAngers

50/63

[MultipartHttpServletRequest] est une interface drive de linterface [HttpServletRequest]. Nous dcrivons certaines des
mthodes utiles pour obtenir des informations sur le document tlcharg :

name : le nom du champ HTML de type " file " qui a servi au tlchargement. Dans notre exemple cest " contenu ".
MultipartFile : une interface qui donne accs au contenu du document tlcharg ainsi qu son nom

Ainsi nous pourrons :

obtenir le nom primitif du document tlcharg par la mthode [getOriginalFileName]


savoir si lutilisateur a choisi un document non vide par la mthode [isEmpty]
copier le document tlcharg dans le systme de fichiers du serveur avec la mthode [transferTo]
obtenir le contenu du document tlcharg par la mthode [getBytes]

Revenons sur la configuration de lintercepteur [MultipartResolver] de notre application :


1.
2.
3.
4.
5.
6.
7.

<!-- upload de fichiers -->


<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize">
<value>1000000</value>
</property>
</bean>

ligne 2 : daprs le Javadoc, la valeur de lid est importante. Sa valeur " multipartResolver " indique que le bean
correspondant est un intercepteur [MultipartResolver].
ligne 3 : limplmentation choisie ici pour lintercepteur est [CommonsMultipartResolver], une classe qui sappuie sur
le projet Apache " commons fileUpload " disponible lurl [http://jakarta.apache.org/commons/fileupload/] :

springmvc - partie3, ST universit dAngers

51/63

Lutilisation du projet " commons fileUpload " ncessite lajout darchives dans le Classpath de lapplication, [commonsfileupload] et [commons-io] :

La classe [CommonsMultipartResolver] dispose de plusieurs mthodes set pour configurer lintercepteur [MultipartResolver] :

La mthode [setMaxUploadSize] sert fixer la taille maximale des documents tlchargs. Dans notre application, nous avons
fix 1 Mo cette taille (ligne 5 de la configuration du MultipartResolver).

4.3

Ecriture du contrleur [UploadController]

Replaons le contrleur [UploadController] dans la chane de traitement de la requte " multipart " quil est charg de traiter :
Couche Interface Utilisateur[ui]
utilisateur

DispatcherServlet

MultipartResolver
2

1
2

UploadController
4

View

Mtier | DAO

Donnes

Modle Map
6

et revenons sur sa configuration :


1.<!-- le contrleur de l'application-->
2. <bean id="UploadController"
3.
class="istia.st.springmvc.exemples.web.UploadController">
4.
<property name="sessionForm">
5.
<value>true</value>
6.
</property>
7.
<property name="commandClass">
8.
<value>istia.st.springmvc.exemples.web.Document</value>
9.
</property>
10. <property name="commandName">
11.
<value>document</value>
12. </property>
springmvc - partie3, ST universit dAngers

52/63

13. <property name="formView">


14.
<value>upload</value>
15. </property>
16. <property name="validator">
17.
<ref local="validateur"/>
18. </property>
19. </bean>

Lignes 7-10, nous voyons que le conteneur des saisies est de type [Document]. Ce type dfini dans [WEB-INF/src] est le
suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

package istia.st.springmvc.exemples.web;
public class Document {
// le nom du document
String nom;
// son contenu
byte[] contenu;
// getters et setters
...
}

Rappelons les deux champs de saisie du formulaire HTML :


1.
<form method="post" enctype="multipart/form-data">
2.
...
3.
<input name="nom" type="text" value="">
4.
...
5.
<input name="contenu" type="file" size="80">
6.
...
7.
<input type="submit" name="action" value="Enregistrer1"></td>
8.
<input type="submit" name="action" value="Enregistrer2"></td>
9. </form>

Si on rapproche ce formulaire du conteneur [Document], on voit que la valeur associe au champ HTML :
1.
2.

" nom " sera mmorise dans le champ [nom] de type String de [Document].
" contenu " sera mmorise dans le champ [contenu] de type byte[] de [Document].

On se rappelle que Spring MVC dispose de conversions implicites des valeurs postes, qui sont de simples chanes de
caractres (sauf dans le cas du champ HTML de type [file]), vers des types autres que String, telles que String -> File, String ->
Class, etc... Lorsquil nexiste pas de conversion implicite, on peut dclarer des classes de conversion dans la mthode
[initBinder] du contrleur charg de traiter la requte. Certaines de ces classes sont fournies par Spring. Cest ainsi que dans un
exemple de larticle 2, nous avions dclar dans [initBinder], la classe Spring [CustomDateEditor] capable de faire les
conversions String -> Date :
binder.registerCustomEditor(java.util.Date.class, null,new CustomDateEditor(dateFormat, false));

On appelle ces classes de conversion des diteurs de proprit, une dnomination provenant de la norme Javabean. Dans le cas
dune requte " multipart ", il nous faut un diteur de proprits particulier qui soit capable dextraire les valeurs postes du
flux multipart envoy par le client et de les affecter aux champs de mmes noms dans le conteneur de saisies. Cet diteur est
fourni par Spring MVC et sappelle [ByteArrayMultipartFileEditor]. Avec cet diteur, le flux " multipart " envoy par le client
que nous avons tudi prcdemment :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

-----------------------------23281168279961
Content-Disposition: form-data; name="nom"
upload
-----------------------------23281168279961
Content-Disposition: form-data; name="contenu"; filename="upload.jsp"
Content-Type: application/octet-stream
....
-----------------------------23281168279961
Content-Disposition: form-data; name="action"
Enregistrer1
-----------------------------23281168279961--

va affecter :

la valeur " upload " au champ [Document].nom (lignes 2-4)

le contenu du document tlcharg (upload.jsp) au champ [Document].contenu (lignes 6-9)


springmvc - partie3, ST universit dAngers

53/63

Le code du contrleur [UploadController] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.

package istia.st.springmvc.exemples.web;
import
import
import
import
import
import

java.io.DataOutputStream;
java.io.File;
java.io.FileOutputStream;
java.io.IOException;
java.util.HashMap;
java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import
import
import
import
import
import
import
import

org.springframework.validation.BindException;
org.springframework.web.bind.RequestUtils;
org.springframework.web.bind.ServletRequestDataBinder;
org.springframework.web.multipart.MultipartFile;
org.springframework.web.multipart.MultipartHttpServletRequest;
org.springframework.web.multipart.support.ByteArrayMultipartFileEditor;
org.springframework.web.servlet.ModelAndView;
org.springframework.web.servlet.mvc.SimpleFormController;

public class UploadController extends SimpleFormController {


// upload de document
protected ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
// on rcupre le document
Document document = (Document) command;
// on rcupre la requte multipart
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
// on rcupre une rfrence sur le fichier charg
MultipartFile fichier = multipartRequest.getFile("contenu");
// fichier dsign ?
if (fichier.isEmpty()) {
// le fichier n'a pas t dsign
errors.rejectValue("contenu", "document.contenu.obligatoire");
// on raffiche le formulaire avec les erreurs
return showForm(request, response, errors);
}
// on rcupre le nom du fichier charg
String nomDocument = fichier.getOriginalFilename();
// le chemin d'enregistrement du document
String chemin = request.getSession().getServletContext().getRealPath(
"/")
+ "uploads\\" + nomDocument;
// on rcupre le nom de l'action en cours
String action = RequestUtils.getStringParameter(request, "action",
"enregistrer1").toLowerCase();
// selon l'action, deux mthodes diffrentes d'enregistrement
if (action.equals("enregistrer1")) {
doEnregistrer1(fichier, chemin);
} else {
doEnregistrer2(document, chemin);
}
// confirmation de l'enregistrement
Map modle = new HashMap();
modle.put("document", document);
modle.put("nomDocument", nomDocument);
return new ModelAndView("confirmation", modle);
}
// enregistrement - mthode 1 - partir de la requte
private void doEnregistrer1(MultipartFile fichier, String chemin)
throws IllegalStateException, IOException {
// on enregistre le flux [fichier] sur le serveur l'emplacement
// [chemin]
fichier.transferTo(new File(chemin));
}
// enregistrement - mthode 2 - partir du document
private void doEnregistrer2(Document document, String chemin)
throws IOException {
// on enregistre le document [document] sur le serveur l'emplacement
// [chemin]
DataOutputStream out = new DataOutputStream(
new FileOutputStream(chemin));
out.write(document.getContenu());
out.close();
}
// enregistrement diteur de proprit
protected void initBinder(HttpServletRequest request,

springmvc - partie3, ST universit dAngers

54/63

84.
ServletRequestDataBinder binder) throws Exception {
85.
// pour que le flux multipart post soit directement affect au champ de type byte[]
86.
binder.registerCustomEditor(byte[].class,
87.
new ByteArrayMultipartFileEditor());
88. }
89.
90. }

ligne 22 : [UploadController] drive de [SimpleFormController]


lignes 83-88 : la mthode [initBinder] dans laquelle nous dclarons lditeur de proprits
[ByteArrayMultipartFileEditor] pour le type byte[]
lignes 25-61 : la mthode [onSubmit] qui sera excute si le conteneur de saisies [Object command] a t dclar
valide par le validateur de type [ValidateurDocument] li au contrleur :
1.
<!-- le contrleur de l'application-->
2.
<bean id="UploadController"
3.
class="istia.st.springmvc.exemples.web.UploadController">
4. ...
5.
<property name="validator">
6.
<ref local="validateur"/>
7.
</property>
8.
</bean>
9.
<!-- validateur -->
10. <bean id="validateur" class="istia.st.springmvc.exemples.web.ValidateurDocument"/>

Nous aurons loccasion de revenir sur ce validateur de donnes. Rappelons que lobjectif de la mthode [onSubmit]
est de :

terminer le traitement la requte [HttpServletRequest request]. Ici il sagira de sauvegarder le document


tlcharg dans le dossier [uploads] de lapplication web, avec le mme nom quil avait sur la machine
cliente.

retourner un objet [ModelAndView] au contrleur gnral [DispatcherServlet]. Ici nous afficherons soit une
page de confirmation de la sauvegarde soit le formulaire de tlchargement avec un message derreur sil y a
eu un problme.

ligne 29 : on rcupre le conteneur des saisies de type [Document]. Celui-ci contient les champs [nom] et [contenu]
contenant respectivement lidentifiant donn au document tlcharg et le contenu de celui-ci.
ligne 31 : on rcupre la requte dont a t extrait [Document]. Cette requte est de type
[MultipartHttpServletRequest] et contient des informations supplmentaires celles prsentes dans [Document] et qui
vont nous tre utiles.
ligne 33 : on rcupre lobjet [MultipartFile fichier] dcrivant le document tlcharg via le champ HTML " contenu "
lignes 35-40 : on vrifie si lutilisateur a bien dsign un fichier et sil la dsign quil est non vide. Si ce nest pas le
cas, un message derreur de code " document.contenu.obligatoire " et associ au champ HTML " contenu " est ajout
la liste derreurs [BindException errors] reue en paramtre (ligne 37). Puis (ligne 39), on redemande laffichage du
formulaire. La mthode [showForm] rend un [ModelAndView] o la vue est celle dfinie par la proprit [formView]
du contrleur et o le modle est form de la liste des erreurs [BindException errors] qui lui est passe en paramtre
ainsi que du conteneur de saisies dfini par la proprit [commandClass] du contrleur. Parce que le contrleur a sa
proprit [sessionForm] true, le conteneur de saisies sera cherch dans la session. Cest donc le mme objet que
lobjet [Object command] reu par la mthode [onSubmit].
ligne 42 : on rcupre le nom quavait le document sur le poste client. Si le document avait un chemin
[elt1/elt2/.../eltN], le nom rcupr est [eltN].
ligne 44 : on construit le chemin o sera sauvegard le document sur le serveur. Il sagit du chemin
<contexte>/uploads/eltN, o <contexte> est le dossier racine de lapplication web. Celle-ci est obtenue partir de la
requte [request] par
request.getSession().getServletContext().getRealPath("/")

ligne 48 : on rcupre la valeur du paramtre HTML nomm " action ". Rappelons o apparat ce paramtre dans le
formulaire HTML :
1.
<form method="post" enctype="multipart/form-data">
2.
...
3.
<input name="nom" type="text" value="">
4.
...
5.
<input name="contenu" type="file" size="80">
6.
...
7.
<input type="submit" name="action" value="Enregistrer1"></td>
8.
<input type="submit" name="action" value="Enregistrer2"></td>
9. </form>

La valeur du paramtre " action " sera " Enregistrer1 " ou " Enregistrer2 " selon le bouton utilis pour valider le
formulaire. On a voulu montrer deux techniques pour enregistrer le document sur le serveur. Le bouton [Enregistrer1]
springmvc - partie3, ST universit dAngers

55/63

va utiliser la requte [MultipartHttpServletRequest] alors que le bouton [Enregistrer2] va, lui, utiliser le conteneur de
saisies.

ligne 52 : enregistrement du document tlcharg partir de lobjet [MultipartFile] extrait de la requte


[MultipartHttpServletRequest]
ligne 54 : enregistrement du document tlcharg partir du conteneur de saisies de type [Document]

lignes 57-60 : construction dun objet [ModelAndView] destination de [DispatcherServlet]. Le nom de la vue est
" confirmation " (ligne 60). Deux lments sont mis dans le modle de cette vue :

le conteneur de saisies associ la cl " document ", ligne 58

le nom quavait le document sur le poste client, ligne 9

lignes 64-69 : enregistrement du document tlcharg encapsul dans lobjet [MultipartFile fichier] lemplacement
[String chemin]. On utilise la mthode [MultipartFile].transferTo pour faire ce travail.

lignes 72-80 : enregistrement du document tlcharg encapsul dans lobjet [Document document] lemplacement
[String chemin]. On passe par lintermdiaire dun flux dcriture [DataOutputStream] qui a une mthode [write]
capable de placer un tableau doctets (byte[]) dans son flux dcriture.

O en sommes-nous ?

larchitecture du projet a t dcrite :


Couche Interface Utilisateur[ui]
utilisateur

DispatcherServlet

MultipartResolver
2

1
2

UploadController
4

View

Mtier | DAO

Donnes

Modle Map
6

la configuration [spring-mvc-36-servlet.xml] du projet a t dcrite :

1.
2.

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/springbeans.dtd">
3. <beans>
4.
<!-- les mappings de l'application-->
5.
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
6.
<property name="mappings">
7.
<props>
8.
<prop key="upload.html">UploadController</prop>
9.
</props>
10.
</property>
11. </bean>
12. <!-- le rsolveur de vues -->
13. <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
14.
<property name="basename">
15.
<value>vues</value>
16.
</property>
17. </bean>
18. <!-- le contrleur de l'application-->
19. <bean id="UploadController"
20.
class="istia.st.springmvc.exemples.web.UploadController">
21. ...
22.
<property name="validator">
23.
<ref local="validateur"/>
24.
</property>
25. </bean>
26. <!-- validateur -->
27. <bean id="validateur" class="istia.st.springmvc.exemples.web.ValidateurDocument"/>
28. <!-- le gestionnaire d'exceptions -->
29. <bean
30.
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
31. ...
32. </bean>
springmvc - partie3, ST universit dAngers

56/63

33. <!-- upload de fichiers -->


34. <bean id="multipartResolver"
35.
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
36. ...
37. </bean>
38. <!-- le fichier des messages -->
39. <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
40.
<property name="basename">
41.
<value>messages</value>
42.
</property>
43. </bean>
44. </beans>

Il nous reste expliciter :

4.4

le validateur de donnes dfini ligne 27


les diffrentes vues de lapplication dfinies dans [vues.properties] (lignes 13-17)
le fichier des messages (lignes 39-43)

Le validateur des donnes postes

La classe [ValidateurDocument] est une classe propritaire dfinie dans [WEB-INF/src] :

Son code est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.

package istia.st.springmvc.exemples.web;

lignes 8-11 : indiquent que le validateur ne sait valider que les types [Document] ou drivs.
lignes 13-20 : la mthode [validate] qui sera appele par [DispatcherServlet] avant que la requte ne soit passe au
contrleur [UploadController].
ligne 15 : le conteneur de saisies est rcupr en tant que type [Document]
ligne 17 : rcupre la valeur poste pour le champ HTML " nom "
lignes 18-20 : vrifie que cette valeur est non vide. Si ce nest pas le cas, une erreur de code
"document.nom.obligatoire" est associe au champ HTML " nom " et mise dans la liste des erreurs [Errors erreurs]
reue en paramtre. On sait que lorsque [DispatcherServlet] rcupre cette liste non vide, il raffiche alors le
formulaire initial en ajoutant son modle la liste des erreurs.

4.5

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class ValidateurDocument implements Validator {
public boolean supports(Class classe) {
// valide les classes de type Document ou driv
return classe.isAssignableFrom(Document.class);
}
public void validate(Object objet, Errors erreurs) {
// on rcupre le document
Document document = (Document) objet;
// on vrifie son nom
String nom = document.getNom();
if (nom == null || nom.trim().length() == 0) {
erreurs.rejectValue("nom", "document.nom.obligatoire");
}
}
}

Le fichier des messages

Daprs la configuration :
1.
<!-- le fichier des messages -->
2.
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
3.
<property name="basename">
springmvc - partie3, ST universit dAngers
57/63

4.
5.
6.

<value>messages</value>
</property>
</bean>

les messages derreur seront trouvs dans un fichier " messages.properties " (ligne 4) situ dans le ClassPath de lapplication
web. Il a t plac dans [WEB-INF/src] :

Son contenu est le suivant :


1.
2.
3.

typeMismatch=Erreur au chargement ...


document.nom.obligatoire=Le nom est obligatoire ...
document.contenu.obligatoire=Vous devez dsigner un fichier non vide ...

Le code (2) est gnr par le contrleur [UploadController] et le code (3) par le validateur [ValidateurDocument]. Le code (1)
est gnr par Spring MVC lorsquil narrive pas " caster " les valeurs postes dans les champs du conteneur [Document].
Normalement ce cas ne devrait pas se produire ici. On le voit apparatre lorsquon supprime la mthode [initBinder] du
contrleur [UploadController] qui dfinit lditeur de proprits capable de " caster " les valeurs postes dans les champs du
conteneur [Document]. Le lecteur est invit faire ce test.

4.6

Les vues de lapplication

Les vues sont dfinies dans [vues.properties] :

de la faon suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.

#upload
upload.class=org.springframework.web.servlet.view.JstlView
upload.url=/WEB-INF/vues/upload.jsp
#confirmation
confirmation.class=org.springframework.web.servlet.view.JstlView
confirmation.url=/WEB-INF/vues/confirmation.jsp
#exception
exception.class=org.springframework.web.servlet.view.JstlView
exception.url=/WEB-INF/vues/exception.jsp

lignes 2-3 : la vue " upload " est la vue du formulaire de tlchargement
lignes 5-6 : la vue " confirmation " est la vue qui confirme le succs du tlchargement
lignes 8-9 : la vue " exception " utilise par le gestionnaire dexceptions lorsquune exception non gre se produit.

Les pages JSP de ces trois vues ont t places dans le dossier [WEB-INF/vues] :

Le code de la page [upload.jsp] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.

<%@
<%@
<%@
<%@

page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>


taglib uri="/WEB-INF/c.tld" prefix="c" %>
taglib uri="/WEB-INF/spring.tld" prefix="spring" %>
page isELIgnored="false" %>

<html>
<head>
<title>Upload (mvc-36)</title>
</head>

springmvc - partie3, ST universit dAngers

58/63

10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.

<body>
<h3>Enregistrement d'un document sur le serveur</h3>
<form method="post" enctype="multipart/form-data">
<table>
<tr>
<td>Nom du document</td>
<spring:bind path="document.nom">
<td>
<input name="${status.expression}" type="text" value="${status.value}">
</td>
<td>
${status.errorMessage}
</td>
</spring:bind>
</tr>
<tr>
<td>Document</td>
<spring:bind path="document.contenu">
<td>
<input name="${status.expression}" type="file" size="80">
</td>
<td>
${status.errorMessage}
</td>
</spring:bind>
</tr>
</table>
<input type="submit" name="action" value="Enregistrer1"></td>
<input type="submit" name="action" value="Enregistrer2"></td>
</form>
</body>

41.</html>

on se rappellera que, par configuration, la cl "document " dsigne le conteneur de saisies de type [Document]
ligne 12 : le formulaire " multipart "
lignes 16-23 : le champ HTML " nom " associ au champ " nom " du conteneur de saisies. Ce champ fait lobjet dune
vrification de validit do lusage de la balise <spring:bind> qui nous permet notamment davoir accs au message
derreur ventuel associ au champ. La vrification est faite dans la mthode [validate] du validateur
[ValidateurDocument] (ligne 32).
lignes 27-34 : le champ HTML "contenu" associ au champ "contenu" du conteneur de saisies qui fait lui aussi lobjet
dune vrification de validit. Celle-ci est faite dans la mthode [onSubmit] du contrleur [UploadController].

Le code de la page [confirmation.jsp] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

<%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>


<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Upload (mvc-36)</title>
</head>
<body>
Le document [${document.nom},${nomDocument}] a &eacute;t&eacute; enregistr&eacute; sur le
serveur.
<br><br>
<a href"upload.html">Recommencer</a>
</body>
</html>

La mthode [onSubmit] du contrleur [UploadController] met dans le modle de la page [confirmation.jsp], le conteneur des
saisies sous la cl " document " et le nom du document sur le poste client sous la cl " nomDocument ". La ligne 10 lidentifiant
du document [document.nom] et son nom primtif sur le poste client [nomDocument].
Le code de la page [exception.jsp] est le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

<%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%>


<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Upload (mvc-36)</title>
</head>
<body>
<h2>Erreur</h2>
L'exception suivante s'est produite :
<c:out value="${exception.message}"/>
<br>
</body>

springmvc - partie3, ST universit dAngers

59/63

15. </html>

Cette page nest utilise que par le gestionnaire dexceptions. Celui-ci est configur (spring-mvc-36-servlet.xml) de la faon
suivante :
1.<!-- le gestionnaire d'exceptions -->
2. <bean
3.
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
4.
<property name="exceptionAttribute">
5.
<value>exception</value>
6.
</property>
7.
<property name="defaultStatusCode">
8.
<value>500</value>
9.
</property>
10. <property name="defaultErrorView">
11.
<value>exception</value>
12. </property>
13. </bean>

la ligne 5 indique que la vue par dfaut utilise pour les exceptions qui remontent jusqu [DispatcherServlet] est la
vue " exception ", donc la page JSP [exception.jsp].
la ligne 11 indique que lexception sera mise dans le modle de la vue sous le nom " exception "

La ligne 12 de [exception.jsp] affiche donc le texte de lexception. On sait en effet que [exception.message] sera traduit par
[request.get("exception").getMessage()].

4.7

Les tests

Aprs ces longues explications, nous sommes prts pour des tests. Ici, le client et le serveur sont sur le mme poste. Nous
prendrons les documents tlchargs dans le dossier [vues] du projet Eclipse. Nous savons que [uploadController] les range
ensuite dans le dossier [uploads] du mme projet. Au dpart, la situation dans le projet Eclipse est la suivante :

Nous demandons lurl [http://localhost:8080/spring-mvc-36/upload.html] :

Ici la mthode 1 denregistrement va tre utilise. La page obtenue en retour est la suivante :

Lidentifiant " upload " du document tlcharg ainsi que le nom originel de celui-ci " upload.jsp " ont t correctement
rcuprs. Nous utilisons le lien [Recommencer] pour faire un second tlchargement :

springmvc - partie3, ST universit dAngers

60/63

Ici la mthode 2 denregistrement va tre utilise. La page obtenue en retour est la suivante :

Si nous revenons au projet Eclipse et que nous le rafrachissons (F5), nous pouvons voir que les documents prcdents ont t
sauvegards dans le dossier [uploads] :

Rappelons la configuration de lintercepteur [MultipartResolver] :


1.<!-- upload de fichiers -->
2. <bean id="multipartResolver"
3.
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
4.
<property name="maxUploadSize">
5.
<value>1000000</value>
6.
</property>
7. </bean>

Les lignes 4-6 indiquent que la taille du document tlcharg ne peut excder 1 Mo. Essayons :

La rponse est la suivante :

springmvc - partie3, ST universit dAngers

61/63

Limplmentation [CommonsMultipartResolver] utilise a lanc une exception qui est remonte jusqu [DispatcherServlet].
Celui-ci a alors fait appel au gestionnaire dexceptions qui a fait afficher la vue " exception " et donc la page JSP
[exception.jsp].

5 Conclusion
Cet article a prsent les points suivants du framework Spring MVC :

le contrleur [MultiActionController] qui permet de rassembler dans un mme contrleur le traitement de diverses
Url. Il convient des cas simples.
le contrleur [AbstractWizardFormController] qui permet de grer des formulaires multi-pages (assistant)
lintercepteur [MultipartResolver] qui permet de grer les documents tlchargs sur le serveur
les gnrateurs de vues [AbstractExcelView] et [AbstractPdfView] qui permettent de produire respectivement des
vues Excel ou PDF

Les principaux points de Spring MVC ont t dsormais exposs. Dans le prochain article, nous mettrons en oeuvre Spring
MVC dans une architecture 3tier [web, metier, dao] sur un exemple basique dclin en plusieurs versions. Ce sera loccasion de
revenir sur les outils offerts par Spring pour grer lensemble des couches dune architecture ntier et qui le distinguent
notamment du framework Struts qui lui, ne soccupe que de la seule couche [web].

6 Le code de l'article
Comme pour les prcdents articles, le lecteur trouvera le code des exemples de ce document sous la forme d'un fichier zipp
sur le site de l'article. Les rgles de dploiement du fichier zipp sont relire dans l'article 1. Une fois un projet import dans
[Eclipse] :

copier le contenu du dossier [lib] du zip dans le dossier [WEB-INF/lib] du projet

s'assurer que le dossier [work] existe sinon le crer : [clic droit sur projet / Projet Tomcat / Crer le dossier work]

nettoyer le projet [Project / clean / clean selected projects]

springmvc - partie3, ST universit dAngers

62/63

Table des matires


1RAPPELS..................................................................................................................................................................................... 2
2ENCORE DES CONTRLEURS.............................................................................................................................................. 3
2.1LE CONTRLEUR PARAMETERIZABLEVIEWCONTROLLER...................................................................................................................3
2.2LE CONTRLEUR URLFILENAMEVIEWCONTROLLER........................................................................................................................ 6
2.3LE CONTRLEUR MULTIACTIONCONTROLLER................................................................................................................................. 8
2.3.1PRSENTATION............................................................................................................................................................................. 8
2.3.2EXEMPLE 1............................................................................................................................................................................... 10
2.3.3EXEMPLE 2 (SPRING-MVC-33B)................................................................................................................................................... 17
2.3.4EXEMPLE 3 (SPRING-MVC-33C)................................................................................................................................................... 19
2.3.5EXEMPLE 4 (SPRING-MVC-33D)...................................................................................................................................................20
2.4LE CONTRLEUR ABSTRACTWIZARDFORMCONTROLLER................................................................................................................ 21
2.4.1PRSENTATION........................................................................................................................................................................... 21
2.4.2LE CONTENEUR DES SAISIES..........................................................................................................................................................27
2.4.3LES VUES DU PROJET...................................................................................................................................................................28
2.4.4LES CLASSES DU PROJET...............................................................................................................................................................31
2.4.5CONCLUSION..............................................................................................................................................................................33
3PRODUIRE DES VUES EXCEL OU PDF............................................................................................................................. 33
3.1INTRODUCTION.............................................................................................................................................................................33
3.2LA VUE HTML.......................................................................................................................................................................... 38
3.3LA VUE EXCEL............................................................................................................................................................................ 38
3.4LA VUE PDF...............................................................................................................................................................................43
4TLCHARGEMENT DE DOCUMENTS VERS LE SERVEUR...................................................................................... 46
4.1INTRODUCTION.............................................................................................................................................................................46
4.2LES INTERCEPTEURS DE TYPE [MULTIPARTRESOLVER]................................................................................................................... 49
4.3ECRITURE DU CONTRLEUR [UPLOADCONTROLLER].......................................................................................................................52
4.4LE VALIDATEUR DES DONNES POSTES.......................................................................................................................................... 57
4.5LE FICHIER DES MESSAGES............................................................................................................................................................ 57
4.6LES VUES DE LAPPLICATION......................................................................................................................................................... 58
4.7LES TESTS................................................................................................................................................................................... 60
5CONCLUSION.......................................................................................................................................................................... 62
6LE CODE DE L'ARTICLE...................................................................................................................................................... 62

springmvc - partie3, ST universit dAngers

63/63