Vous êtes sur la page 1sur 70

Spring MVC par l'exemple - Partie 1 -

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

springmvc - partie1, serge.tahe@istia.univ-angers.fr

1/70

1 Gnralits
Dans le dveloppement web, la mthodologie de dveloppement MVC (Modle-Vue-Contrleur) est dsormais bien ancre. Rappelons-en le principe. Une application web a souvent une architecture 3tier :

utilisateur

Couche interface utilisateur [ui]

Couche mtier [metier]

Couche d'accs aux donnes [dao]

Donnes

la couche [dao] s'occupe de l'accs aux donnes, le plus souvent des donnes persistantes au sein d'un SGBD. Mais cela peut tre aussi des donnes qui proviennent de capteurs, du rseau, ... la couche [metier] implmente les algorithmes " mtier " de l'application. Cette couche est indpendante de toute forme d'interface avec l'utilisateur. Ainsi elle doit tre utilisable aussi bien avec une interface console, une interface web, une interface de client riche. Elle doit ainsi pouvoir tre teste en-dehors de l'interface web et notamment avec une interface console. C'est gnralement la couche la plus stable de l'architecture. Elle ne change pas si on change l'interface utilisateur ou la faon d'accder aux donnes ncessaires au fonctionnement de l'application. la couche [interface utilisateur] qui est l'interface (graphique souvent) qui permet l'utilisateur de piloter l'application et d'en recevoir des informations.

La communication va de la gauche vers la droite : l'utilisateur fait une demande la couche [interface utilisateur] cette demande est mise en forme par la couche [interface utilisateur] et transmise la couche [mtier] si pour traiter cette demande, la couche [mtier] a besoin des donnes, elle les demande la couche [dao] chaque couche interroge rend sa rponse la couche de gauche jusqu' la rponse finale l'utilisateur. Les couches [mtier] et [dao] sont normalement utilises via des interfaces Java. Ainsi la couche [mtier] ne connat de la couche [dao] que son ou ses interfaces et ne connat pas les classes les implmentant. C'est ce qui assure l'indpendance des couches entreelles : changer l'implmentation de la couche [dao] n'a aucune incidence sur la couche [mtier] tant qu'on ne touche pas la dfinition de l'interface de la couche [dao]. Il en est de mme entre les couches [interface utilisateur] et [mtier]. L'architecture MVC prend place dans la couche [interface utilisateur] lorsque celle-ci est une interface web. Des articles (par exemple : http://tahe.developpez.com/java/m2vc) ont montr qu'on pouvait galement appliquer le paradigme MVC une couche [interface utilisateur] Swing. Au sein de l'architecture 3tier, l'architecture MVC peut tre reprsente comme suit : Couche Interface Utilisateur [ui] utilisateur
1

Contrleur
4 3

Modle
5

Couche mtier [metier]

Couche d'accs aux donnes [dao]

Donnes

Vue
6

Le traitement d'une demande d'un client se droule selon les tapes suivantes : 1. 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. 2. le contrleur C traite cette demande. 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 3. 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.
springmvc - partie1, serge.tahe@istia.univ-angers.fr

2/70

lui fournir les donnes dont il a besoin pour gnrer cette rponse. En effet, celle-ci contient le plus souvent des informations calcules par le contrleur. Ces informations forment ce qu'on appelle le modle M de la vue, le M de MVC. L'tape 3 consiste donc en le choix d'une vue V et en la construction du modle M ncessaire celle-ci. 4. le contrleur C demande la vue choisie de s'afficher. Il s'agit le plus souvent de faire excuter une mthode particulire de la vue V charge de gnrer la rponse au client. Dans ce document, nous appelerons vue, aussi bien l'objet qui gnre la rponse au client que cette rponse elle-mme. La littrature MVC n'est pas explicite sur ce point. Si c'est la rponse qui devait s'appeler vue, on pourrait appeler gnrateur de vue, l'objet qui gnre cette rponse. 5. le gnrateur de vue V utilise le modle M prpar par le contrleur C pour initialiser les parties dynamiques de la rponse qu'il doit envoyer au client. 6. 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, ...

La mthodologie de dveloppement web MVC ne ncessite pas ncessairement d'outils externes. On peut ainsi dvelopper une application web Java avec une architecture MVC avec un simple JDK et les bibliothques de base du dveloppement web. Une mthode utilisable pour des applications simples est la suivante :

le contrleur est assur par une servlet unique. C'est le C de MVC. toutes les requtes du client contiennent un attribut action, par exemple (http://.../appli?action=liste). selon la valeur de l'attribut action, la servlet fait excuter une mthode interne de type [doAction(...)]. la mthode [doAction] excute l'action demande par l'utilisateur. Pour cela, si besoin est, elle utilise la couche [mtier]. selon le rsultat de l'excution, la mthode [doAction] dcide d'une page JSP afficher. C'est la vue V du modle MVC. la page JSP a des lments dynamiques qui doivent tre fournis par la servlet. La mthode [doAction] va fournir ces lments. C'est le modle de la vue, le M de MVC. Ce modle est plac le plus souvent dans le contexte de la requte (request.setAttribute(" cl ", "valeur "), voire moins frquemment, dans le contexte de la session ou de l'application. On sait que la page JSP a accs ces trois contextes. la mthode [doAction] fait afficher la vue en transmettant le flux d'excution la page JSP choisie. Elle utilise pour cela, une instruction du genre [getServletContext() .getRequestDispatcher(" pageJSP ").forward(request, response)].

Pour des applications simples, dveloppes par un individu unique, cette mthode est suffisante. Nanmoins, lorsqu'on a crit plusieurs applications de ce type, on s'aperoit que les servlets de deux applications diffrentes : 1. ont le mme mcanisme pour dterminer quelle mthode [doAction] il faut excuter pour traiter l'action demande par l'utilisateur 2. ne diffrent en fait que par le contenu de ces mthodes [doAction] La tentation est alors grande de : factoriser le traitement (1) dans une servlet gnrique ignorante de l'application qui l'utilise dlguer le traitement (2) des classes externes puisque la servlet gnrique ne sait pas dans quelle application elle est utilise faire le lien entre l'action demande par l'utilisateur et la classe qui doit la traiter l'aide d'un fichier de configuration Des outils, souvent appels " frameworks ", sont apparus pour apporter les facilits prcdentes aux dveloppeurs. Le plus ancien et probablement le plus connu d'entre-eux est Struts (http://struts.apache.org/). Jakarta Struts est un projet de l'Apache Software Foundation (www.apache.org). Ce framework est dcrit dans (http://tahe.developpez.com/java/struts/). Apparu plus rcemment, le framework Spring (http://www.springframework.org/) offre des facilits analogues celles de Struts. Le but de cet article est de prsenter les possibilits de Spring MVC, la branche de Spring consacre au dveloppement web MVC. Une diffrence importante entre Struts et Spring est le champ d'applications de ces deux outils. Struts ne sert qu' construire le modle MVC dans la couche [interface web]. Spring offre lui, des outils pour le dveloppement des trois couches d'une application 3tier. Couche Interface Utilisateur[ui]
1

utilisateur

Contrleur
4 3

Modle
5

Couche mtier [metier]

Couche d'accs aux donnes [dao]

Donnes

Vue
6

SPRING Ci-dessus, on a reprsent Spring comme une couche transversale de l'application 3tier afin d'illustrer le fait qu'il offre des outils pour le dveloppement des trois couches. Struts lui, se cantonne uniquement la couche [interface utilisateur]. springmvc - partie1, serge.tahe@istia.univ-angers.fr 3/70

On peut utiliser conjointement Spring et Struts : Spring pour l'intgration des couches, la gestion des transactions dans la couche [mtier], l'accs aux donnes dans la couche [dao], ... Struts dans la couche [interface utilisateur] Il peut cependant tre tentant de n'utiliser qu'un seul " framework " et d'utiliser dans la couche [interface utilisateur], Spring MVC en lieu et place de Struts. L'article " Variations autour d'une architecture web trois couches " ([http://tahe.developpez.com/java/web3tier]), prsente une application utilisant les trois architectures voques ici : une servlet propritaire traitant les actions avec des mthodes internes une architecture Struts une architecture Spring MVC Cet article prsente Spring MVC. Nous utiliserons une succession d'exemples simples pour illustrer les diffrentes facettes du produit et nous terminerons par la construction d'une application web MVC 3tier qui soit un peu raliste.

2 Outils utiliss et rfrences


Les outils utiliss pour les exemples ont t les suivants :

Eclipse 3.01 pour le dveloppement des applications Java, disponible l'url [http://www.eclipse.org]. Plugin Eclipse : Sysdeo Tomcat pour dvelopper des applications web avec le conteneur de servlets Tomcat, disponible l'url [http://www.sysdeo.com/eclipse/tomcatplugin]. Spring 1.2.4 disponible aux url [http://www.springframework.org/download]. On prendra la version de [Spring] avec dpendances car [Spring] utilise de nombreux outils tiers dont les archives sont contenues dans la version "avec dpendances". Tomcat 5 comme conteneur de servlets, disponible l'url [http://jakarta.apache.org].

Dans une chelle [dbutant-intermdiaire-avanc], ce document est dans la partie [intermdiaire]. Sa comprhension ncessite divers pr-requis. Certains d'entre-eux peuvent tre acquis avec des documents que j'ai crits. Dans ce cas, je les cite. Il est bien vident que ce n'est qu'une suggestion et que le lecteur peut utiliser ses documents favoris.

[ref1] : programmation web en Java [http://tahe.developpez.com/java/web/] [ref2] : utilisation d'Eclipse avec Tomcat 5 : [http://tahe.developpez.com/java/eclipse/]. Ce document prsente la version de Tomcat 5.0. Celle actuellement disponible est la 5.5.15 (mars 2006). Dans cette version, le lien [Tomcat administration] de la page d'accueil [http://localhost:8080] de Tomcat, ne fonctionne plus comme dans la version 5.0. En-dehors de ce point, les explications du document prcdent restent valides. Le prsent article n'utilise pas le lien [Tomcat administration]. [ref3] : documentation Spring : [http://www.springframework.org/documentation] [ref4] : utilisation de l'aspect IoC de Spring : [http://tahe.developpez.com/java/springioc] [ref5] : applications web 3tier avec une architecture MVC : [http://tahe.developpez.com/java/web3tier]

Pour crire cet article, je me suis servi de la documentation officielle de Spring ainsi que des deux ouvrages suivants : 1. 2. [ref6] : Professional Java Development with the Spring Framework, par Rod Johnson, Juergen Hoeller, Alef Arendsen, Thomas Risberg, Colin Sampaleanu, chez l'diteur Wiley [ref7] : Pro Spring, par Rob Harrop et Jan Machacek, chez l'diteur APress

Un livre est paru rcemment (fvrier 2006) sur Spring MVC [ref8] : Expert Spring MVC and Web Flow, par Seth Ladd, Darren Davison, Steven Devijver, Colin Yates aux ditions APress. Je n'ai pas eu l'occasion de le lire. A ma connaissance, c'est le premier livre sorti sur ce sujet. A ceux qui sont fchs avec l'anglais, je propose de lire l'article qui suit. Pour les autres, les trois rfrences prcdentes seront certainement utiles pour approfondir le sujet.

3 Le code des exemples


Le code des exemples qui vont suivre au mme endroit que cet article sous la forme d'un zip. Une fois celui-ci dzipp, on obtient l'arborescence suivante :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

4/70

Les dossiers [mvc-xx] sont des projets Eclipse. Ils ont tous un dossier [lib] vide. Celui-ci doit normalement contenir des archives .jar. Celles-ci tant de taille assez importante, on a viter de les dupliquer en les mettant uniquement dans le dossier [lib] ci-dessus :

Pour tester les diffrents projets sous Eclipse, on peut procder de la faon suivante. Sous Eclipse, importons par exemple le projet [mvc-02]. On clique droit dans [Package Explorer] :

Nous prenons l'option [Import ] :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

5/70

faire [Next]

faire [Browse] pour aller dsigner le projet [mvc-02] :

faire [OK]

faire [Finish]

Le projet s'ouvre dans [Package Explorer] :


springmvc - partie1, serge.tahe@istia.univ-angers.fr

6/70

Ci-dessus, la croix rouge indique que le projet est erron. Ceci parce que le dossier [lib] est vide. Avec l'explorateur windows, copier le contenu du dossier [lib] du fichier dzipp dans [mvc-02/WEB-INF/lib] :

Revenir sous Eclipse. Cliquer droit sur le nom du projet [spring-mvc-02] et prendre [Refresh] :

Cela force le projet se reconstruire :

Ci-dessus, le projet ne prsente plus d'erreurs.


springmvc - partie1, serge.tahe@istia.univ-angers.fr

7/70

Faire du projet [spring-mvc-02] une application Tomcat (clic droit sur projet) :

Pour le tester, lancer [Tomcat] :

Avec un navigateur, demander l'url [http://localhost:8080/spring-mvc-02/dosomething.html] :

Pour passer d'un projet l'autre, on ritre la dmarche prcdente. Pour permettre Tomcat de dmarrer plus vite, il est prfrable de supprimer le contexte des projets tests :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

8/70

Pour recrer ce contexte ultrieurement, il suffir de prendre l'option [Mise jour du contexte].

4 Les composantes d'une architecture Spring MVC


Revenons sur l'architecture d'une application web MVC : Couche Interface Utilisateur[ui] utilisateur
1

Contrleur
4 3

Modle
5

Couche mtier [metier]

Couche d'accs aux donnes [dao]

Donnes

Vue
6

Spring MVC implmente cette architecture de la faon suivante : Couche Interface Utilisateur[ui] utilisateur
1

DispatcherServlet
2 3 4

Couche mtier [metier]

Couche d'accs aux donnes [dao]

Donnes

Controller
5

Modle Map
6

View
7

1.

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 9/70

springmvc - partie1, serge.tahe@istia.univ-angers.fr

2.

3.

4.

5.

6. 7.

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 ci-dessus 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, ...

Examinons le traitement d'une demande du client sous un aspect un peu plus technique : Etapes 1-2 : choix du contrleur traitant l'action demande par l'utilisateur Avec Spring MVC, c'est partir de l'URL demande par le client que le contrleur principal [DispatcherServlet] va choisir l'objet [Controller] qui va traiter l'action. Le lien URL <-> Controller est fait par configuration. Il existe plusieurs mthodes de rsolution possibles, c.a.d. plusieurs faons possibles de lier une URL un objet [Controller]. Etapes 3-5 : traitement de la demande par l'objet [Controller] L'objet [Controller] charg de traiter l'action [org.springframework.web.servlet.mvc.Controller]. est une instance de classe implmentant l'interface

Spring offre plusieurs classes implmentant l'interface [Controller] :

Ces classes sont normalement destines tre drives. On se souvient en effet, que le contrleur est charg de traiter une action d'une application spcifique. Ceci ne peut tre fait dans les classes gnriques ci-dessus sauf lorsqu'il n'y a pas de traitement faire (ParameterizableViewController, ServletForwardingController). De faon gnrale, c'est au dveloppeur de construire la classe implmentant l'interface [Controller]. Il peut parfois se faire aider en drivant cette classe des classes ci-dessus. C'est notamment le cas si l'action demande est le POST d'un formulaire. Dans ce cas particulier important, la classe [SimpleFormController] apporte des facilits de dveloppement. L'interface [Controller] n'a qu'une mthode :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

10/70

l'unique mthode implmenter par un contrleur est donc [handleRequest] qui a pour paramtres, l'objet [request] qui encapsule la requte HTTP du client, et l'objet [response] qui lui encapsule la rponse HTTP qui sera faite ce mme client. comme le montre le schma plus haut, un contrleur doit dsigner une vue V afficher en rponse la demande du client et le modle M qui sera affich par cette vue. Ces deux lments sont rendus par [handleRequest] sous la forme d'un objet de type [ModelAndView] plus exactement [org.springframework.web.servlet.ModelAndView].

Un objet de type [ModelAndView] peut tre construit de diverses faons :

1 2 3 4 5 6 7

1. 2.

3. 4. 5.

le constructeur [1] ne dfinit ni modle, ni vue. Ceux-ci sont alors dfinis ultrieurement via les mthodes de la classe [ModelAndView] le constructeur [2] dfinit une vue V d'aprs son nom. Nous savons qu'au final, la vue est un objet implmentant l'interface [org.springframework.web.servlet.View]. Le lien nom <-> objet View est alors fait dans un fichier de configuration. Le constructeur [2] ne prcise pas de modle. Il convient donc pour une vue sans modle. On peut aussi prciser le modle ultrieurement via des mthodes de [ModelAndView]. le constructeur [3] dfinit une vue V d'aprs son nom et dfinit galement un modle M sous la forme d'un objet implmentant l'interface [java.util.Map], donc un dictionnaire. le constructeur [4] est une variante du constructeur [3] lorsque le modle n'a qu'un unique lment. Dans ce cas, le dictionnaire n'a qu'un lment dont la cl sera [modelName] et la valeur associe [modelObject]. les constructeurs [5, 6, 7] sont des variantes des constructeurs [2, 3, 4] dans lesquelles, la vue V n'est plus dsigne par son nom mais directement instancie par un objet implmentant l'interface [View]. L'association nom vue <-> objet View n'a alors plus tre faite dans le fichier de configuration.

Les mthodes de la classe [ModelAndView] sont les suivantes :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

11/70

1 2

les mthodes [1] et [2] permettent d'ajouter des lments au modle M

3 4

les mthodes [3] et [4] permettent de prciser la vue V soit par une instance View [3], soit par le nom de la vue [4].

Etapes 6-7 : rendu du modle M par la vue V Arriv ici, le flux d'excution a t transfr une vue [View]. [View] est une interface avec l'unique mthode suivante :

assez logiquement, l'unique mthode [render] dispose de l'objet [response] partir duquel la rponse HTTP va tre construite et du modle [model] construit prcdemment par le contrleur. La mthode [render] dispose galement de l'objet [request] qui encapsule la requte HTTP du client. Via cet objet, la mthode [render] a accs au contexte de la requte, la session qui lui est lie, ... L'une des implmentations de l'interface [View] est la classe [JstlView] qui implmente la vue l'aide d'une page JSP utilisant des balises JSTL. La mthode [render] de la classe [JstlView] met les lments du dictionnaire [model] qu'elle a reu dans le contexte de la requte [request]. C'est l que la page JSP qui sera envoye au client trouvera son modle.

Spring propose plusieurs implmentations de l'interface [View] :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

12/70

Des noms de classes ci-dessus, on peut dduire que Spring offre des classes permettant d'avoir des vues

de type HTML (JstlView), Excel, PDF destines des frameworks d'affichage : Velocity, FreeMarker, Tiles

Dans la suite, nous prsenterons des exemples illustrant les diffrentes tapes de traitement d'une requte dcrites ci-dessus.

5 Configuration d'une application Spring MVC


Une application Spring MVC est une application web. Elle obit donc aux rgles de configuration de ce type d'applications. Cidessous, nous montrons comment est configure une application Spring MVC. L'environnement de dveloppement utilis est Eclipse. Le lecteur fera la part des choses entre ce que l'architecture dcrite doit Eclipse et qui est propre Eclipse et ce qui ressort de la configuration ncessaire de toute application web, quelque soit l'environnement de dveloppement utilis. Les applications qui vont suivre ont t construites sous Eclipse avec le plugin Sysdeo Tomcat afin de pouvoir lancer, arrter, relancer Tomcat partir d'Eclipse. Le dveloppement web avec Eclipse et Tomcat est dcrit dans [ref2] (cf page 4). Les diffrents projets que nous allons construire sous Eclipse seront tous des projets de type [Projet Tomcat]. Rappelons comment se cre un tel projet :

on dmarre l'assistant de cration par [File / New / Project ] :

on choisit le type [Projet Tomcat] on indique le nom du projet et son emplacement dans le systme de fichiers :

on donne un nom (contexte) l'application web :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

13/70

et c'est fini :

Le projet apparat dans l'explorateur de projets d'Eclipse :

Avec un clic droit sur le nom du projet, on a accs diverses proprits du projet Tomcat :

Les options utiles sont les suivantes : Mise jour du contexte : cre le fichier de dfinition de l'application qui indique Tomcat que cette application fait partie des applications qu'il doit grer. Suppression du contexte : supprime le fichier prcdent. Cela vite Tomcat de charger des ressources pour une application qui n'est temporairement plus utilise par exemple. Cela acclre galement le dmarrage de Tomcat. Recharger le contexte : demande Tomcat de recharger le contexte de l'application parce qu'on y a apport une modification. L'application est alors recharge sans avoir relancer Tomcat. Nous dcrivons maintenant une structure typique des applications Spring MVC que nous allons construire.

springmvc - partie1, serge.tahe@istia.univ-angers.fr

14/70

WEB-INF/src Ce dossier contiendra les classes des applications web. C'est le plugin Tomcat qui l'impose. Nous mettrons les classes Java toujours dans le mme paquetage [istia.st.springmvc.exemples.web]. Les classes compiles sont automatiquement places dans le dossier [WEB-INF/classes], l o les attend le serveur web Tomcat. Tous les fichiers du dossier [WEB-INF/src] autres que les classes .java sont automatiquement recopies en l'tat dans [WEBINF/classes]. Dans une application web, le dossier [WEB-INF/classes] fait partie du " Classpath " de l'application web. Aussi mettra-t-on dans [WEB-INF/src] tous les fichiers qui doivent tre dans le Classpath de l'application. C'est le cas du fichier [log4j.properties] ci-dessus. Ce fichier est recherch dans le Classpath par l'archive [log4j-1.2.9.jar] que l'on voit dans la copie d'cran ci-dessus. Cette archive contient les classes de l'outil log4j souvent utilis pour afficher des logs. log4j est configur l'aide du fichier [log4j.properties]. A ma grande honte, j'avoue ne pas bien connatre log4j. J'ai utilis le fichier [log4j.properties] minimal suivant :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. # Global logging configuration log4j.rootLogger=INFO, stdout # configuration... org.apache.commons.digester.Digester=INFO # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

En l'absence de ce fichier, Spring affichait sur la console un message disant que log4j n'tait pas configur. Il prcisait de plus que c'tait la classe [org.apache.commons.digester.Digester] qui voulait faire des logs. Aprs quelques recherches sur le web, j'ai cr le fichier prcdent. Les lignes sont standard : on les retrouve dans tous les exemples de [log4j.properties]. J'ai utilis le message de Spring pour la ligne 5 qui elle est spcifique. Ceci fait, Spring ne s'est plus plaint et a fait ses logs sur la console. Je m'en suis tenu l et c'est ce mme fichier qui sera utilis dans toutes les applications. WEB-INF/lib Ce dossier est facultatif. J'y mets toutes les archives .jar dont l'application a besoin :

spring.jar : la totalit de l'archive Spring 1.2.4 [http://www.springframework.org] commons-*.jar : bibliothques " commons " utilises par Spring [http://jakarta.apache.org/commons/] jstl.jar, standard.jar : bibliothques ncessaires pour l'utilisation des balises JSTL dans les pages JSP [http://jakarta.apache.org/taglibs/] log4j-1.2.9.jar : bibliothque de logs [http://logging.apache.org/log4j/docs/]

Le fait de mettre ces archives dans [WEB-INF/lib] n'a aucune signification particulire. Pour que l'application les reconnaisse, il faut les mettre dans son Classpath. Sous Eclipse, on procde comme suit : 1. clic droit sur le nom du projet / Properties / Java Build Path

springmvc - partie1, serge.tahe@istia.univ-angers.fr

15/70

2.

utiliser [Add Jars] pour ajouter les archives dsires. Eclipse indique alors les archives du Classpath dans sa modlisation du projet :

WEB-INF/vues Nous utiliserons ce dossier pour y mettre les pages JSP qui serviront de vues l'application. Ces vues peuvent tre mises n'importe o dans le dossier de l'application web. En les mettant sous le dossier [WEB-INF] on interdit un utilisateur extrieur de demander directement une vue JSP. Il devra passer obligatoirement par l'une des actions dfinies pour l'application. WEB-INF Ce dossier est obligatoire dans une application web Java. Il comporte au moins deux lments : [WEB-INF/classes] qui contient les fichiers .class ncessaires l'application web.xml : le fichier de configuration de l'application Le fichier [web.xml] sera pour toutes les applications construit sur le modle suivant :
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> 14. <servlet-name>spring-mvc-10</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-10</servlet-name> 21. <url-pattern>*.html</url-pattern> springmvc - partie1, serge.tahe@istia.univ-angers.fr

16/70

22. </servlet-mapping> 23. </web-app>

lignes 8-11 : dfinissent une classe qui est charge au dmarrage de l'application. La classe [org.springframework.web.context.ContextLoaderListener] est une classe Spring charge d'initialiser le contexte de l'application web en lisant le contenu du fichier [WEB-INF/applicationContext.xml]. Ce fichier nous servira instancier les couches [mtier] et [dao] lorsqu'il y en a. Lorsque l'application n'aura que la couche [interface utilisateur] et pas les couches [mtier] et [dao], le fichier [WEB-INF/applicationContext.xml] n'existera pas, ni les lignes 8-11 ci-dessus. Ce sera souvent le cas dans nos applications exemple o il n'y aura le plus souvent que la seule couche d'interface web. lignes 13-14 : dfinissent une servlet : ligne 14 : nom de la servlet ligne 16 : classe de la servlet. Avec Spring MVC, cette classe est toujours la classe [org.springframework.web.servlet.DispatcherServlet] qui joue le rle du contrleur C du MVC. lignes 19-22 : dsignent les URL gres par l'application : ligne 21 : indique que toute URL termine par .html est gre par l'application ligne 20 : dsigne la servlet charge de grer les URL dfinies ligne 21.

Le fichier [web.xml] ci-dessus indique donc que : toute URL termine par .html doit tre traite par la servlet appele [ spring-mvc-10 ] que la servlet [ spring-mvc-10 ] dsigne la classe [org.springframework.web.servlet.DispatcherServlet], une classe de Spring Le contrleur [org.springframework.web.servlet.DispatcherServlet] associ la servlet [spring-mvc-10] et charg de traiter les URL *.html, a besoin d'informations pour traiter les demandes de l'utilisateur. Il a par exemple besoin de connatre pour chaque URL [ur1.html], l'objet [Controller] charg de la traiter. Nous avons vu qu'un objet [Controller] allait dsigner la vue renvoyer l'utilisateur par son nom. Le contrleur [DispatcherServlet] aura donc besoin de savoir qu' tel nom de vue correspond telle page JSP. D'autres informations seront galement ncessaires. Nous les dtaillerons progressivement. Le contrleur [org.springframework.web.servlet.DispatcherServlet] associ la servlet [spring-mvc-10] trouvera ces informations dans le fichier [WEB-INF/spring-mvc-10-servlet.xml]. Le fichier porte donc le nom de la servlet. Il aura un contenu qui pourra ressembler ceci :
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. <?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="/dosomething.html">DoSomethingController</prop> </props> </property> </bean> <!-- les contrleurs de l'application--> <bean id="DoSomethingController" class="istia.st.springmvc.exemples.web.DoSomething"> <property name="groupe"> <ref bean="groupe"/> </property> </bean> <!-- le rsolveur de vues --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass"> <value>org.springframework.web.servlet.view.JstlView</value> </property> <property name="prefix"> <value>/WEB-INF/vues/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> </beans>

Il est trop tt pour entrer dans les dtails mais voici les grandes lignes de ce fichier : lignes 5-11 : indiquent que l'URL se terminant par [/dosomething.html] doit tre trait par l'objet [Controller] nomm [DoSomethingController]. Nous avons vu que [web.xml] redirigeait les URL *.html vers [DispatcherServlet]. Le fichier [servlet-mvc-10-servlet.xml] prcise les choses : seule l'URL [*/dosomething.html] sera accepte. Les autres seront refuses. lignes 13-18 : dfinissent la classe associe au contrleur de nom [DoSomethingController]. Cette classe est crite par le dveloppeur pour les besoins spcifiques de l'application et implmente l'interface [Controller]. La classe l'implmentant est ici [istia.st.springmvc.exemples.web.DoSomething], une classe propritaire qu'on trouve dans [WEB-INF/src] sur la copie d'cran du projet Eclipse. springmvc - partie1, serge.tahe@istia.univ-angers.fr 17/70

lignes 20-30 : dfinissent comment partir du nom d'une vue, on trouve la page JSP associe. Si [DoSomethingController] demande l'affichage d'une vue appele V, c'est la page JSP [WEB-INF/vues/V.jsp] qui sera affiche.

Pour terminer, la copie d'cran du projet montre la prsence du fichier [c.tld] dans le dossier [WEB-INF]. Ce fichier dfinit la syntaxe des balises de la bibliothque JSTL. Les pages JSP utilisent souvent cette bibliothque de balises qui permet de rduire la prsence de code Java dans la page JSP. Les pages JSP auront un contenu analogue au 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-10</title> </head> <body> <h2>Vue n 0</h2> Membres du groupe <table> <c:forEach var="personne" items="${groupe.membres}"> <tr> <td>${personne}</td> </tr> </c:forEach> </table> <br> DoSomething excut en ${dure} ms... </body> </html>

la ligne 1 est facultative. Elle prcise le type de codage utilis pour les caractres du fichier. ISO-8859-1 autorise les caractres accentus. la ligne 2 indique o trouver un fichier de dfinition de balises. Ici c'est le fichier [c.tld] dont on vient de parler. la ligne 3 indique que les expressions ${identificateur} (par exemple ${personne}ci-dessus) doivent tre interprtes. Selon la version JSTL utilise et celle des pages JSP, cette balise est parfois ncessaire, parfois pas. Avec les versions de JSTL et JSP utilises pour les exemples, elle tait ncessaire.

6 La liaison URL - Controller


6.1 Les stratgies de rsolution des URL

Reprenons l'architecture d'une application Spring MVC : Couche Interface Utilisateur[ui] utilisateur
1

DispatcherServlet
2 3 4

Couche mtier [metier]

Couche d'accs aux donnes [dao]

Donnes

Controller
5

Modle Map
6

View
7

1. 2.

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

Comment le contrleur [DispatcherServlet] sait-il quel objet [Controller] doit traiter la demande du client ? La demande du client est contenue dans l'URL demande au serveur et ventuellement dans les donnes qui l'accompagnent (dans la cas du requte HTTP POST ou plus rarement HTTP PUT). Spring se sert de l'URL pour dterminer quel objet [Controller] doit springmvc - partie1, serge.tahe@istia.univ-angers.fr 18/70

traiter la demande. Spring met notre disposition diverses stratgies pour lier une URL un objet [Controller]. Le choix d'une stratgie particulire est fait dans le fichier de configuration [S-servlet.xml] de la servlet S de l'application Spring MVC en prcisant le nom d'une classe implmentant l'interface [org.springframework.web.servlet.HandlerMapping]. Spring offre les classes d'implmentation suivantes :

[BeanNameUrlHandlerMapping] : avec cette classe, une URL de la forme [http://machine:port/chemin1/.../document] sera traite par le Controller nomm [document]. C'est la stratgie de rsolution par dfaut si aucune stratgie n'est dfinie dans le fichier de configuration de la servlet. [SimpleUrlHandlerMapping] : avec cette classe, chaque URL [http://machine:port/chemin1/.../document ] devant tre traite par l'application est dclare dans le fichier de configuration de la servlet. Dans cette dclaration , on associe [/document] le nom de l'objet [Controller] qui doit la traiter.

Les deux stratgies permettent de spcifier des URL gnriques, par exemple /erreur*.html. La stratgie [SimpleUrlHandlerMapping] permet des URL gnriques plus complexes que celles permises par la stratgie [BeanNameUrlHandlerMapping]. Par ailleurs, la stratgie [SimpleUrlHandlerMapping] permet d'externaliser dans un fichier les associations URL <-> Controller. Nous prsentons tout d'abord un exemple utilisant la stratgie [SimpleUrlHandlerMapping].

6.2

La stratgie SimpleUrlHandlerMapping

Le projet Eclipse de l'exemple est le suivant :

L'application est dfinie par le fichier [web.xml] suivant :


1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. <?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> <!-- la servlet --> <servlet> <servlet-name>spring-mvc-02</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <!-- le mapping des url --> <servlet-mapping> <servlet-name>spring-mvc-02</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>

Ce fichier a dj t expliqu. Simplement notons que la servlet de l'application s'appelle [spring-mvc-02] et que donc son fichier de configuration doit s'appeler [spring-mvc-02-servlet.xml]. Ce fichier est prsent sur la copie d'cran ci-dessus. Son contenu est le suivant :
1. 2. 3. 4. 5. 6. <?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">

springmvc - partie1, serge.tahe@istia.univ-angers.fr

19/70

7. <props> 8. <prop key="/dosomething.html">DoSomethingController</prop> 9. </props> 10. </property> 11. </bean> 12. <!-- les contrleurs de l'application--> 13. <bean id="DoSomethingController" 14. class="istia.st.springmvc.exemples.web.DoSomething"/> 15. </beans>

lignes 5-11 : dfinissent la stratgie de rsolution des URL, ici la stratgie [ SimpleUrlHandlerMapping ]. On remarquera que le bean de la ligne 5 n'a pas de d'attribut " id ", attribut qui identifie les beans. On pourrait lui en donner un mais ce n'est pas obligatoire car il n'est pas rfrenc par ailleurs dans le fichier. Spring MVC instancie automatiquement un certain nombre de beans. C'est le cas des beans dfinissant la stratgie de rsolution, c'est dire des beans implmentant l'interface [HandlerMapping]. ligne 6-9 : dfinissent les liaisons URL <-> Controller de l'application. On y trouve toutes les URL que l'application doit traiter avec, associ, le nom de l'objet Controller qui doit les traiter. ligne 8 : indique que l'URL se terminant par [/dosomething.html] doit tre traite par le Controller de nom [DoSomethingController]. lignes 13-14 : dfinissent le Controller de nom [DoSomethingController] (attribut id). On y indique que ce Controller est une instance de la classe [istia.st.springmvc.exemples.web.DoSomething], une classe propritaire que nous allons dfinir maintenant.

La classe [DoSomething] a t place dans [WEB-INF/src] comme le montre le projet Eclipse. Sa dfinition est la 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. package istia.st.springmvc.exemples.web; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class DoSomething implements Controller { // gestion de la requte public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // on code en dur response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println( "<html><head><title>Spring-MVC-02</title></head>"+ "<body>DoSomething excut...</body></html>"); // pas de ModelAndView return null; } }

Rappelons tout d'abord qu'un objet [Controller] doit implmenter l'interface [Controller]. C'est ce que fait la classe ci-dessus, ligne 10.

lignes 13-23 : dfinissent la mthode [ handleRequest ] qui est l'unique mthode de l'interface [Controller]. Cette mthode doit rendre un couple (Vue, Modle) sous la forme d'un objet de type [ModelAndView]. Cet objet est ensuite utilis par le contrleur [DispatcherServlet] pour envoyer une rponse au client. Ici nous dcidons d'envoyer la rponse nous-mmes. C'est possible puisque nous disposons du paramtre [ HttpServletResponse response ] qui encapsule la rponse HTTP destine au client. ligne 16 : prcise que le flux qui va tre envoy est un flux HTML ligne 17 : rcupre le flux d 'criture vers le client lignes 18-20 : le flux HTML est envoy ligne 22 : on retourne au contrleur [DispatcherServlet] le pointeur null comme objet [ModelAndView]. Le contrleur interprte ce pointeur null comme le fait que la rponse au client a t envoye et qu'il ne doit pas s'en occuper.

Nous sommes prts pour un test. Nous lanons Tomcat :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

20/70

Dans la console, Spring affiche de nombreux logs qu'il est souvent intressant de lire. Ceux lis l'application [servlet-mvc-02] sont les suivants :
INFO [http-8080-Processor23] - Initializing servlet 'spring-mvc-02' INFO [http-8080-Processor23] - FrameworkServlet 'spring-mvc-02': initialization started INFO [http-8080-Processor23] - Loading WebApplicationContext for Spring FrameworkServlet 'spring-mvc-02' INFO [http-8080-Processor23] - Loading XML bean definitions from ServletContext resource [/WEB-INF/spring-mvc-02servlet.xml] INFO [http-8080-Processor23] - Bean factory for application context [WebApplicationContext for namespace 'spring-mvc-02servlet']: org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping,DoSomethingController]; root of BeanFactory hierarchy INFO [http-8080-Processor23] - 2 beans defined in application context [WebApplicationContext for namespace 'spring-mvc02-servlet'] INFO [http-8080-Processor23] - JDK 1.4+ collections available INFO [http-8080-Processor23] - Commons Collections 3.x available INFO [http-8080-Processor23] - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@1d314cc] INFO [http-8080-Processor23] - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@31d5e2] INFO [http-8080-Processor23] - No ThemeSource found for [WebApplicationContext for namespace 'spring-mvc-02-servlet']: using ResourceBundleThemeSource INFO [http-8080-Processor23] - Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping,DoSomethingController]; root of BeanFactory hierarchy] INFO [http-8080-Processor23] - Creating shared instance of singleton bean 'org.springframework.web.servlet.handler.SimpleUrlHandlerMapping' INFO [http-8080-Processor23] - Creating shared instance of singleton bean 'DoSomethingController' INFO [http-8080-Processor23] - Using context class [org.springframework.web.context.support.XmlWebApplicationContext] for servlet 'spring-mvc-02' INFO [http-8080-Processor23] - Unable to locate MultipartResolver with name 'multipartResolver': no multipart request handling provided INFO [http-8080-Processor23] - Unable to locate LocaleResolver with name 'localeResolver': using default [org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@1f31ad9] INFO [http-8080-Processor23] - Unable to locate ThemeResolver with name 'themeResolver': using default [org.springframework.web.servlet.theme.FixedThemeResolver@1c0cd80] INFO [http-8080-Processor23] - No HandlerAdapters found in servlet 'spring-mvc-02': using default INFO [http-8080-Processor23] - No ViewResolvers found in servlet 'spring-mvc-02': using default INFO [http-8080-Processor23] - FrameworkServlet 'spring-mvc-02': initialization completed in 1592 ms INFO [http-8080-Processor23] - Servlet 'spring-mvc-02' configured successfully

Avec un navigateur, nous demandons l'URL [http://localhost:8080/spring-mvc-02/dosomething.html] :

Expliquons ce qui s'est pass : 1. 2. 3. le fichier [web.xml] de l'application [spring-mvc-02] a t consult. Ce fichier indique que les URL *.html doivent tre traites par la servlet de nom [spring-mvc-02] et instancie par [DispatcherServlet]. le contrleur [DispatcherServlet] prend la main et exploite le contenu du fichier [spring-mvc-02-servlet.html]. Il voit que l'URL [/dosomething.html] doit tre traite par le contrleur [istia.st.springmvc.exemples.web.DoSomething]. Celui a t instanci au dmarrage de l'application. [DispatcherServlet] appelle la mthode [handleRequest] de ce contrleur. la mthode [handleRequest] du contrleur [istia.st.springmvc.exemples.web.DoSomething] envoie le flux HTML que nous voyons ci-dessus et rend le pointeur [null] au contrleur principal [DispatcherServlet]. Celui-ci comprend que la rponse a t envoye. Le cycle [demande client - rponse serveur] est termin.

Voil... Notre premire application Spring a t crite. Les briques de base de toute application Spring MVC commencent se mettre en place.

6.3

La stratgie BeanNameUrlHandlerMapping
21/70

springmvc - partie1, serge.tahe@istia.univ-angers.fr

Le projet Eclipse de l'exemple est le suivant :

L'application est dfinie par le fichier [web.xml] suivant :


1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. <?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> <!-- la servlet --> <servlet> <servlet-name>spring-mvc-03</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <!-- le mapping des url --> <servlet-mapping> <servlet-name>spring-mvc-03</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>

La servlet de l'application s'appelle [spring-mvc-03] et donc son fichier de configuration doit s'appeler [spring-mvc-03-servlet.xml]. Ce fichier est prsent sur la copie d'cran ci-dessus. Son contenu est le suivant :
1. 2. 3. 4. 5. 6. 7. <?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 name="/dosomething.html" class="istia.st.springmvc.exemples.web.DoSomething"/> </beans>

Il n'y a plus de stratgies de rsolution. On sait qu'alors c'est la stratgie [BeanNameUrlHandlerMapping] qui est utilise. C'est dire qu'une url [/URL] est traite par le bean contrleur d'attribut name [/URL]. Dans une balise <bean>, l'attribut name sert galement identifier un bean comme le fait l'attribut id. Seulement l'attribut id a des rgles de syntaxe qui ne permettent pas d'crire [id= "/dosomething.html "], cause du signe /. On utilise donc l'attribut name. Ci-dessus, l'url [/dosomething.html] sera traite par le contrleur d'attribut name [/dosomething.html] et donc par la classe [istia.st.springmvc.exemples.web.DoSomething]. Cette classe est celle de l'application prcdente. Nous ne changeons rien sauf le titre de la page HTML que nous changeons en Spring-MVC-03. Nous sommes prts pour le test. Nous lanons Tomcat si besoin est, puis demandons l'URL [http://localhost:8080/spring-mvc03/dosomething.html] :

Expliquons ce qui s'est pass : 1. le fichier [web.xml] de l'application [spring-mvc-03] a t consult. Ce fichier indique que les URL *.html doivent tre traites par la servlet de nom [spring-mvc-03] dont la classe associe est [DispatcherServlet]. 22/70

springmvc - partie1, serge.tahe@istia.univ-angers.fr

2.

3.

le contrleur [DispatcherServlet] prend la main et exploite le contenu du fichier [spring-mvc-03-servlet.html]. Sans stratgie de rsolution dfinie [spring-mvc-03-servlet.html], il utilise la stratgie par dfaut [BeanNameUrlhandlerMapping]. Il cherche donc un contrleur d'id [/dosomething.html]. Il le trouve. [DispatcherServlet] appelle la mthode [handleRequest] de ce contrleur. la mthode [handleRequest] du contrleur [istia.st.springmvc.exemples.web.DoSomething] envoie le flux HTML que nous voyons ci-dessus et rend le pointeur [null] au contrleur principal [DispatcherServlet]. Celui-ci comprend que la rponse a t envoye. Le cycle [demande client - rponse serveur] est termin.

6.4

La stratgie SimpleUrlHandlerMapping avec liaisons externalises

Le projet Eclipse de l'exemple est le suivant :

L'application est dfinie par un fichier [web.xml] analogue au prcdent, mais dsormais la servlet s'appelle [spring-mvc-04]. Le fichier de configuration [spring-mvc-04-servlet.xml] est le suivant :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. <?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"> <bean class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="location"> <value>mappings.properties</value> </property> </bean> </property> </bean> <!-- les contrleurs de l'application--> <bean id="DoSomethingController" class="istia.st.springmvc.exemples.web.DoSomething"/> </beans>

la ligne 15 indique que nous sommes revenus la stratgie de rsolution [ SimpleUrlHandlerMapping ]. Cependant, les liens URL <-> Controller ne sont pas dans le mme fichier mais dans le fichier de proprits indiqu ligne 10.

Le fichier [mappings.properties] se trouve la racine du dossier de l'application (cf copie d'cran) et son contenu est le suivant :
## mappings de l'application spring-mvc-04 /dosomething.html=DoSomethingController

Ce fichier a des lignes de la forme : Url=Controller, qui associent un objet Controller une Url. On ne fait ici qu'externaliser des informations qui dans l'application [spring-mvc-02] taient dans le fichier [spring-mvc-02-servlet.xml]. En-dehors de ce point, les applications spring-mvc-02 et spring-mvc-04 sont identiques. Pour le test, nous demandons l'URL [http://localhost:8080/spring-mvc-04/dosomething.html] :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

23/70

7 Accs au contexte d'une application Spring MVC


7.1 Exemple 1

Reprenons l'architecture d'une application Spring MVC : Couche Interface Utilisateur[ui] utilisateur
1

DispatcherServlet
2 3 4

Couche mtier [metier]

Couche d'accs aux donnes [dao]

Donnes

Controller
5

Modle Map
6

View
7

Pour traiter la demande d'un client, l'objet [Controller] a besoin d'informations de diffrente porte et situs pour cette raison dans des conteneurs diffrents :

le contexte de l'application (javax.servlet.ServletContext) contient les objets qui sont partags entre tous les utilisateurs. Parce qu'ils sont partags entre tous les utilisateurs, ils sont le plus souvent en lecture seule. Leur dure de vie est celle de l'application. la session (javax.servlet.http.Session) contient les objets appartenant un utilisateur donn. Celui fait au serveur des requtes successives qui sont considres chaque fois par le serveur comme une nouvelle requte. Le mcanisme de la session permet d'avoir une mmoire entre les diffrentes requtes d'un mme utilisateur. le contexte de la requte (javax.servlet.http.HttpServletRequest) contient les objets d'une requte donne d'un utilisateur donn. L'objet [HttpRequest] qui l'encapsule peut tre trait par une chane de servlets. Le contexte de la requte permet une servlet de passer des informations la servlet suivante de la chane.

Lors de son dmarrage, une application web a besoin de construire son environnement d'excution. Il s'agit d'une phase d'initialisation qui aboutit mettre dans le contexte de l'application, des objets partags par tous les utilisateurs, par exemple cidessus, les objets instanciant les couches [mtier] et [dao]. O peut se faire cette initialisation ? Lorsque la servlet d'une application web est charge par le conteneur de servlets, sa mthode [init] est excute. C'est dans la spcification des servlets. Ce sera donc le cas galement pour la servlet [DispatcherServlet] d'une application Spring MVC. Nous n'avons pas la matrise du contrleur [DispatcherServlet]. Pour accder sa mthode [init] qui nous permettrait d'initialiser l'application, nous sommes amens driver la classe [DispatcherServlet] afin de redfinir sa mthode [init]. C'est ce que montre l'exemple que nous dcrivons maintenant. Le projet Eclipse est le suivant :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

24/70

L'application web [spring-mvc-06] est configure par le fichier [web.xml] suivant :


1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. <?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> <!-- la servlet --> <servlet> <servlet-name>spring-mvc-06</servlet-name> <servlet-class> istia.st.springmvc.exemples.web.MyServlet</servlet-class> <init-param> <param-name>groupe</param-name> <param-value>Paul,Mlanie,Jacques</param-value> </init-param> </servlet> <!-- le mapping des url --> <servlet-mapping> <servlet-name>spring-mvc-06</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>

Ce fichier [web.xml] ne diffre des prcdents que par sa ligne 11. Le contrleur C du MVC n'est plus de type [DispatcherServlet] comme jusqu' maintenant mais de type [ istia.st.springmvc.exemples.web.MyServlet ], un type propritaire dfini dans [WEBINF/src] (cf copie d'cran). Pour ne rien perdre des capacits de [DispatcherServlet], la classe [MyServlet] en drive. Nous drivons [DispacherServlet] afin d'avoir accs aux paramtres d'initialisation de l'application dfinis lignes 12-15 ci-dessus. Il s'agit de dfinir un groupe de personnes. La ligne de la classe [DispatcherServlet] est la suivante :

La classe [DispatcherServlet] ne dfinit pas elle-mme de mthode [init]. Dans la ligne ci-dessus, la mthode [init] apparat tout d'abord dans [GenericServlet]. Les classes drives [HttpServlet, HttpServletBean] la redfinissent. Seulement la classe [ HttpServletBean ] la redfinit en la dclarant [final], ce qui interdit qu'elle soit redfinie de nouveau dans les classes drives de [HttpServletBean]. Nous ne pouvons donc utiliser la mthode [init] pour initialiser l'application Spring MVC. En consultant la dfinition de la classe [DispatcherServlet], on trouve une mthode [initFrameworkServlet] qui peut tre utilise pour le faire :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

25/70

Lorsque cette mthode est appele, le contexte de l'application [applicationContext.xml] a dj t exploit. Par ailleurs, la mthode instancie diffrents objets ncessaires l'application, notamment sa stratgie de rsolution des URL (HandlerMapping). En redfinissant cette mthode, on peut ajouter de plus des initialisations " propritaires ". La classe [MyServlet], que nous allons utiliser comme contrleur principal de l'application, et drive de la classe [DispatcherServlet] est la suivante :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. package istia.st.springmvc.exemples.web; import javax.servlet.ServletException; import org.springframework.beans.BeansException; import org.springframework.web.servlet.DispatcherServlet; public class MyServlet extends DispatcherServlet { public void initFrameworkServlet() throws BeansException, ServletException { // init parent super.initFrameworkServlet(); // on rcupre le paramtre de [web.xml] String[] groupe = (this.getServletConfig().getInitParameter("groupe")) .split(","); // on met le tableau dans le contexte de l'application this.getServletContext().setAttribute("groupe", groupe); }

ligne 8 : la classe drive de [DispatcherServlet] lignes 10-18 : redfinissent la mthode [ initFrameworkServlet ] de la classe [DispatcherServlet] ligne 12 : nous laissons la mthode [ initFrameworkServlet ] de la classe parent faire son travail lignes 13-17 : nous ajoutons nos initialisations propritaires ligne 14 : on rcupre dans [web.xml], le paramtre (init-param) qui porte le nom " groupe ". On obtient une chane de caractres constitue de sous-chanes spares par une virgule. Ces sous-chanes sont rcupres dans le tableau groupe. ligne 17 : le tableau groupe est mis dans le contexte de l'application afin qu'il soit visible par tous les utilisateurs de l'application.

Le fichier [web.xml] dfinissait une servlet appele [servlet-mvc-06]. Le fichier de configuration [servlet-mvc-06-servlet.xml] de celle-ci est le suivant :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. <?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="/dosomething.html">DoSomethingController</prop> </props> </property> </bean> <!-- les contrleurs de l'application--> <bean id="DoSomethingController" class="istia.st.springmvc.exemples.web.DoSomething"/> </beans>

Il n'y a rien ici qu'on ne connaisse dj. L'url [/dosomething.html] va tre traite par le contrleur [ istia.st.springmvc.exemples.web.DoSomething ]. Celui-ci est dans [WEB-INF/src] (cf copie d'cran). Le code du contrleur [DoSomething] est le suivant :
springmvc - partie1, serge.tahe@istia.univ-angers.fr

26/70

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.

package istia.st.springmvc.exemples.web; import java.io.PrintWriter; import java.util.Date; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class DoSomething implements Controller { // gestion de la requte public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // dbut long dbut = new Date().getTime(); // attente Thread.sleep(10); // on code en dur response.setContentType("text/html"); PrintWriter out = response.getWriter(); // on prpare le code HTML String Html = "<html><head><title>Spring-MVC-06</title></head>" + "<body>"; // on parcourt la liste des membres du groupe String[] groupe = (String[]) request.getSession().getServletContext().getAttribute("groupe"); for (int i = 0; i < groupe.length; i++) { Html += groupe[i] + "<br>\n"; } // fin long fin = new Date().getTime(); // dure long dure = fin - dbut; Html += "<br>DoSomething excut en " + dure + " ms...</body></html>"; // on envoie le flux HTML out.println(Html); // pas de ModelAndView return null; } }

ligne 12 : [DoSomething] implmente l'interface [Controller]. lignes 15-42 : redfinissent la mthode [HandleRequest] qui sera appele par le contrleur [DispatcherServlet] ligne 18 : on note l'heure de dbut du traitement ligne 20 : on attend 10 ms ligne 22 : on indique que la rponse sera un flux HTML lignes 23 : le flux d'criture vers le client lignes 25 - 26 : dbut du flux HTML ligne 28 : on rcupre l'attribut " groupe " dans le contexte de l'application. lignes 29-31 : les membres du groupe sont crits dans le flux HTML ligne 33 : on note l'heure de fin du traitement ligne 35 : la dure du traitement ligne 36 : la dure est crite dans le flux HTML ligne 38 : envoi du flux HTML au client ligne 40 : on rend le pointeur [null] [DispatcherServlet] pour lui indiquer que la rponse a t envoye.

Nous sommes prts pour un test. Aprs avoir lanc Tomcat, nous demandons l'url [http://localhost:8080/spring-mvc06/dosomething.html] :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

27/70

7.2

Exemple 2

Reprenons l'architecture d'une application Spring MVC : Couche Interface Utilisateur[ui] utilisateur
1

DispatcherServlet
2 3 4

Couche mtier [metier]

Couche d'accs aux donnes [dao]

Donnes

Controller
5

Modle Map
6

View
7

Les diffrents objets [Controller] qui traitent les demandes des utilisateurs ont normalement besoin de communiquer avec la couche mtier (opration 3 ci-dessus). Celle-ci est accde en gnral via une ou plusieurs interfaces. L'instanciation de celles-ci peut tre faite par configuration avec Spring IoC. Les instances cres doivent tre places dans le contexte de l'application car ce sont des singletons partags par tous les utilisateurs. Le fichier [web.xml] utilis prcdemment pour crer le contexte de l'application n'est pas un fichier Spring et ne peut donc tre utilis pour instancier les couches [mtier] et [dao] avec Spring IoC. Spring MVC propose donc une autre approche. Nous avons vu que les applications prcdentes taient associes une servlet S et un fichier de configuration [S-servlet.xml]. On pourrait vouloir instancier la couche [mtier] dans ce dernier fichier. Cependant, une application Spring MVC peut utiliser diverses servlets S1, S2, ... donnant naissance des fichiers [S1-servlet.xml, S2-servlet.xml, ...]. Afin que les singletons des couches [mtier] et [dao] soient accessibles aux diffrentes servlets, on dfinit ces singletons dans un fichier de configuration Spring " parent " des fichiers de servlets [Si-servlet.xml]. Spring permet en effet de crer une relation parent - fils entre deux fichiers de configuration. Les beans dfinis dans le fichier parent sont automatiquement connus du fichier fils. Dans une application Spring MVC, le fichier [applicationContext.xml] joue le rle de " parent " des fichiers [S1-servlet.xml, S2servlet.xml, ...] de dfinition des servlets de l'application. C'est donc dans ce fichier qu'on placera en gnral la configuration des couches [mtier] et [dao] de l'application. Le fichier [applicationContext.xml] doit tre plac dans le dossier [WEB-INF]. Pour que le fichier [applicationContext.xml] soit reconnu et exploit, l'application Spring MVC doit charger une classe spciale de type [org.springframework.web.context.ContextLoaderListener]. Cette classe va exploiter le fichier [applicationContext.xml] et instancier les beans qui y sont dfinis. Parce que ceux-ci doivent tre instancis avant ceux dfinis par les fichiers [Si-servlet.xml], la classe [org.springframework.web.context.ContextLoaderListener] doit tre instancie avant la classe [DispatcherServlet] qui elle, va exploiter les fichiers [Si-servlet.xml]. Cela peut tre obtenu au moyen d'un fichier [web.xml] analogue au 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="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-05</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <!-- le mapping des url --> <servlet-mapping> <servlet-name>spring-mvc-05</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>

Ce fichier est semblable aux prcdents sauf en ce qui concerne les lignes 8-11. Ces lignes dfinissent un " listener " d'application qui sera instanci ds le dmarrage de l'application et avant les classes associes aux servlets. Ceci nous assure que les beans dfinis dans [applicationContext.xml] seront instancis avant ceux dfinis dans les fichiers [Si-servlet.xml].
springmvc - partie1, serge.tahe@istia.univ-angers.fr

28/70

Pour illustrer le rle du fichier [applicationContext.xml], nous utiliserons le projet Eclipse suivant :

On notera la prsence du fichier [applicationContext.xml] dans [WEB-INF]. Le fichier [web.xml] de l'application est celui qui vient d'tre dcrit. Le fichier [applicationContext.xml] est, lui, le suivant :
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> <!-- une liste de personnes --> <bean id="groupe" class="istia.st.springmvc.exemples.web.Groupe"> <property name="membres"> <list> <value>Paul</value> <value>Mlanie</value> <value>Jacques</value> </list> </property> </bean> </beans>

Il dfinit un bean " groupe ", objet de type [istia.st.springmvc.exemples.web.Groupe]. Cette classe, dfinie dans [WEB-INF/src] (cf copie d'cran) est dfinie comme suit :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. package istia.st.springmvc.exemples.web; import java.util.ArrayList; public class Groupe { private ArrayList membres; public ArrayList getMembres() { return membres; } public void setMembres(ArrayList membres) { this.membres = membres; }

ligne 7 : un champ priv de type [ArrayList] lignes 9 - 15 : getter / setter associs

Le fichier [applicationContext.xml] dfinit un bean d'id " groupe " qui est une instance de la classe [Groupe] avec pour lments du champ priv [membres], les chanes de caractres : " Paul ", " Mlanie ", " Jacques ". Le fichier de configuration [servlet-mvc-05-servlet.xml] de la servlet [spring-mvc-05] est le suivant :
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="/dosomething.html">DoSomethingController</prop> </props>

springmvc - partie1, serge.tahe@istia.univ-angers.fr

29/70

10. </property> 11. </bean> 12. <!-- les contrleurs de l'application--> 13. <bean id="DoSomethingController" 14. class="istia.st.springmvc.exemples.web.DoSomething"> 15. <property name="groupe"> 16. <ref bean="groupe"/> 17. </property> 18. </bean> 19. </beans>

lignes 5-11 : dfinissent [SimpleUrlHandlerMapping] comme stratgie de rsolution des URL lignes 13-18 : dfinissent le contrleur charg de traiter l'url [/dosomething.html]. Ce contrleur est une instance de la classe [ istia.st.springmvc.exemples.web.DoSomething ]. On voit (lignes 15-17) que ce contrleur a une proprit nomme " groupe " qui reoit pour valeur (ligne 16), la valeur d'un bean d'id " groupe ". Il s'agit du bean instanci par [applicationContext.xml]. La relation parent - fils qui lie les fichiers [applicationContext.xml] et [servlet-mvc-05servlet.xml] fait que le bean " groupe " dfini dans [applicationContext.xml] peut tre utilis dans [servlet-mvc-05servlet.xml].

Il ne nous reste plus qu' prsenter le contrleur [ istia.st.springmvc.exemples.web.DoSomething ] :


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. package istia.st.springmvc.exemples.web; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Date; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class DoSomething implements Controller { // un groupe de personnes fourni par le contexte de l'application private Groupe groupe; public Groupe getGroupe() { return groupe; } public void setGroupe(Groupe groupe) { this.groupe = groupe; } // gestion de la requte public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // dbut long dbut = new Date().getTime(); // attente Thread.sleep(10); // on code en dur response.setContentType("text/html"); PrintWriter out = response.getWriter(); // on prpare le code HTML String Html="<html><head><title>Spring-MVC-05</title></head>"+ "<body>"; // on parcourt la liste des membres du groupe ArrayList membres=groupe.getMembres(); for(int i=0;i<membres.size();i++){ Html+=membres.get(i).toString()+"<br>\n"; } // fin long fin = new Date().getTime(); // dure long dure = fin - dbut; Html += "<br>DoSomething excut en " + dure + " ms...</body></html>"; // on envoie le flux HTML out.println(Html); // pas de ModelAndView return null; } }

Ce contrleur est quasi identique celui de l'exemple prcdent. Les diffrences sont les suivantes :

lignes 15 - 21 : dfinissent un champ priv [groupe] de type [Groupe] avec ses getter et setter. Nous avons vu que ce champ tait initialis dans [servlet-mvc-05-servlet.xml] par le bean d'id " groupe " dfini dans [applicationContext.xml]. Le champ priv [groupe] du contrleur va donc tre initialis avec une instance de classe de type [Groupe] dont le champ priv " membres " contiendra les chanes de caractres : " Paul ", " Mlanie ", " Jacques ". Lorsque la mthode [handleRequest] 30/70

springmvc - partie1, serge.tahe@istia.univ-angers.fr

est excute, le champ priv [groupe] aura donc dj sa valeur alors que dans la version prcdente il fallait rcuprer cette valeur dans le contexte de l'application. Testons. Demandons l'url [http://localhost:8080/spring-mvc-05/dosomething.html] :

7.3

Exemple 3

Dans les deux versions prcdentes, les membres du groupe afficher : exemple 1 : taient dfinis dans [web.xml], placs dans le contexte de l'application par le contrleur principal [MyServlet] puis rcuprs par le contrleur [doSomething] dans le contexte de l'application exemple 2 : taient dfinis dans [applicationContext.xml], et injects directement dans le contrleur [doSomething] par configuration du contrleur [DoSomething] dans [servlet-mvc-05-servlet.xml] Dans ce nouvel exemple, les membres du groupe seront dfinis dans [applicationContext.xml] comme dans l'exemple 2 mais pas injects dans le contrleur [DoSomething]. Nous voulons montrer comment le contrleur [DoSomething] peut avoir accs au contenu du fichier [applicationContext.xml]. Le projet Eclipse est le suivant :

Nous voyons la prsence du fichier [applicationContext.xml]. Son contenu est identique celui de la version prcdente. Il en est de mme pour le fichier [web.xml] en-dehors du fait que celui-ci rfrence dsormais une servlet appele [spring-mvc-07B]. Le fichier de configuration [spring-mvc-07B-servlet.xml] de celle-ci a le contenu suivant :
<?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="/dosomething.html">DoSomethingController</prop> 9. </props> 10. </property> 11. </bean> 12. <!-- les contrleurs de l'application--> 13. <bean id="DoSomethingController" 14. class="istia.st.springmvc.exemples.web.DoSomething"> 15. </bean> 16. </beans> springmvc - partie1, serge.tahe@istia.univ-angers.fr 1. 2.

31/70

On a l, un contenu analogue celui de [spring-mvc-05-servlet.xml], si ce n'est que le bean " groupe " n'est plus inject dans le contrleur [DoSomething]. Comment celui-ci peut-il avoir accs au contenu de [applicationContext.xml] ? Pour accder au contenu du fichier [applicationContext.xml], le contrleur peut utiliser la classe [org.springframework.web.servlet.support.RequestContextUtils]. Cette classe offre des mthodes utilitaires statiques dont la suivante :

On voit qu' partir de la requte du client, on est capable d'avoir accs au contexte de l'application Spring MVC. Le contrleur [DoSomething] sera 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. package istia.st.springmvc.exemples.web; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Date; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import org.springframework.web.servlet.support.RequestContextUtils; public class DoSomething implements Controller { // gestion de la requte public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // dbut long dbut = new Date().getTime(); // attente Thread.sleep(10); // on rcupre le bean groupe Groupe groupe=(Groupe)RequestContextUtils.getWebApplicationContext(request).getBean("groupe"); // on code le HTML en dur response.setContentType("text/html"); PrintWriter out = response.getWriter(); // on prpare le code HTML String Html="<html><head><title>Spring-MVC-07</title></head>"+ "<body>"; // on parcourt la liste des membres du groupe ArrayList membres=groupe.getMembres(); for(int i=0;i<membres.size();i++){ Html+=membres.get(i).toString()+"<br>\n"; } // fin long fin = new Date().getTime(); // dure long dure = fin - dbut; Html += "<br>DoSomething excut en " + dure + " ms...</body></html>"; // on envoie le flux HTML out.println(Html); // pas de ModelAndView return null; } }

Ce contrleur est analogue celui des exemples prcdents. Notons les diffrences :

ligne 24 : la classe [ RequestContextUtils ] est utilise pour retrouver le bean " groupe " dfini dans [applicationContext.xml].

Un test d'excution est le suivant :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

32/70

7.4

Exemple 4

Spring met notre disposition diverses classes prdfinies implmentant l'interface [Controller]. Elles disposent de certaines caractristiques qu'il peut tre intressant d'exploiter en les drivant. L'une des classes de Spring implmentant l'interface [Controller] est la classe [AbstractController] :

La mthode [handleRequest] de cette classe est appele par le contrleur [DispatcherServlet] ou driv. Dans son implmentation, la mthode [handleRequest] de [AbstractController] appelle la mthode [handleRequestInternal]. Cette mthode n'a pas de contenu dans [AbstactController] et est dclare abstraite. Sa signature est la suivante :

Elle a donc la mme signature que la mthode [handleRequest]. Le nouveau projet Eclipse est le suivant :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

33/70

Le contenu du fichier [applicationContext.xml] est identique celui de la version prcdente. Il en est de mme pour le fichier [web.xml] en-dehors du fait que celui-ci rfrence dsormais une servlet appele [spring-mvc-07]. Le contenu de [spring-mvc-07servlet.xml] est identique celui de [spring-mvc-07B-servlet.xml]. Le contrleur [DoSomething] sera 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. package istia.st.springmvc.exemples.web; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Date; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; public class DoSomething extends AbstractController { // un groupe de personnes fourni par le contexte de l'application private Groupe groupe; // gestion de la requte public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { // dbut long dbut = new Date().getTime(); // attente Thread.sleep(10); // on rcupre le bean groupe groupe=(Groupe)getWebApplicationContext().getBean("groupe"); // on code le HTML en dur response.setContentType("text/html"); PrintWriter out = response.getWriter(); // on prpare le code HTML String Html="<html><head><title>Spring-MVC-07</title></head>"+ "<body>"; // on parcourt la liste des membres du groupe ArrayList membres=groupe.getMembres(); for(int i=0;i<membres.size();i++){ Html+=membres.get(i).toString()+"<br>\n"; } // fin long fin = new Date().getTime(); // dure long dure = fin - dbut; Html += "<br>DoSomething excut en " + dure + " ms...</body></html>"; // on envoie le flux HTML out.println(Html); // pas de ModelAndView return null; } }

Une nouvelle fois, ce contrleur est trs proche de celui des versions prcdentes. Notons les diffrences :

ligne 13 : le contrleur drive de [AbstractController]. Ceci nous permet ligne 25 de rcuprer les membres du groupe dfinis dans le fichier [applicationContext.xml]. On utilise pour cela la mthode [getWebApplicationContext], une mthode dfinie dans la classe [org.springframework.context.support.ApplicationObjectSupport], une classe parent de [AbstractController]. lignes 18-45 : redfinissent la mthode [handleRequestInternal] de la classe [AbstractController].

Pour le test, nous demandons l'URL [http://localhost:8080/spring-mvc-07/dosomething.html] :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

34/70

8 Gestion des vues


8.1 La place des vues dans l'architecture Spring MVC

L'architecture Spring MV a t dcrite au paragraphe 4, page 9. Nous revenons sur cette architecture afin d'y situer la place des vues. Ce qui suit est un simple copier-coller de ce qui a t prsent au paragraphe 4, page 9 mais nous avons voulu viter au lecteur de revenir en arrire et attirer son attention sur des points qui, lorsqu'ils ont t exposs, taient peut-tre assez flous dans son esprit. Le lecteur averti peut lui passer directement au paragraphe 8.2, page 38. L'architecture Spring MVC est la suivante : Couche Interface Utilisateur[ui] utilisateur
1

DispatcherServlet
2 3 4

Couche mtier [metier]

Couche d'accs aux donnes [dao]

Donnes

Controller
5

Modle Map
6

View
7

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 ci-dessus 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, ...

Etapes 1-2 : choix du contrleur traitant l'action demande par l'utilisateur Avec Spring MVC, c'est partir de l'URL demande par le client que le contrleur principal [DispatcherServlet] va choisir l'objet [Controller] qui va traiter l'action. Le lien URL <-> Controller est fait par configuration. Il existe plusieurs mthodes de rsolution possibles, c.a.d. plusieurs faons possibles de lier une URL un objet [Controller]. Diverses mthodes de rsolution des Url (HandlerMapping) ont t prsentes.
springmvc - partie1, serge.tahe@istia.univ-angers.fr

35/70

Etapes 3-5 : traitement de la demande par l'objet [Controller] L'objet [Controller] charg de traiter l'action [org.springframework.web.servlet.mvc.Controller]. est une instance de classe implmentant l'interface

Spring offre plusieurs classes implmentant l'interface [Controller] :

Ces classes sont normalement destines tre drives. On se souvient en effet, que le contrleur est charg de traiter une action d'une application spcifique. Ceci ne peut tre fait dans les classes gnriques ci-dessus sauf lorsqu'il n'y a pas de traitement faire (ParameterizableViewController, ServletForwardingController). De faon gnrale, c'est au dveloppeur de construire la classe implmentant l'interface [Controller]. Il peut parfois se faire aider en drivant cette classe des classes ci-dessus. C'est notamment le cas si l'action demande est le POST d'un formulaire. Dans ce cas particulier important, la classe [SimpleFormController] apporte des facilits de dveloppement. L'interface [Controller] n'a qu'une mthode :

l'unique mthode implmenter par un contrleur est donc [handleRequest] qui a pour paramtres, l'objet [request] qui encapsule la requte HTTP du client, et l'objet [response] qui, lui, encapsule la rponse HTTP qui sera faite ce mme client. comme le montre le schma plus haut, un contrleur doit dsigner une vue V afficher en rponse la demande du client et le modle M qui sera affich par cette vue. Ces deux lments sont rendus par [handleRequest] sous la forme d'un objet de type [ModelAndView] plus exactement [org.springframework.web.servlet.ModelAndView].

Un objet de type [ModelAndView] peut tre construit de diverses faons :

1 2 3 4 5 6 7

springmvc - partie1, serge.tahe@istia.univ-angers.fr

36/70

1. 2.

3. 4. 5.

le constructeur [1] ne dfinit ni modle, ni vue. Ceux-ci sont alors dfinis ultrieurement via les mthodes de la classe [ModelAndView] le constructeur [2] dfinit une vue V d'aprs son nom. Nous savons qu'au final, la vue est un objet implmentant l'interface [org.springframework.web.servlet.View]. Le lien nom <-> objet View est alors fait dans un fichier de configuration. Le constructeur [2] ne prcise pas de modle. Il convient donc pour une vue sans modle. On peut aussi prciser le modle ultrieurement via des mthodes de [ModelAndView]. le constructeur [3] dfinit une vue V d'aprs son nom et dfinit galement un modle M sous la forme d'un objet implmentant l'interface [java.util.Map], donc un dictionnaire. le constructeur [4] est une variante du constructeur [3] lorsque le modle n'a qu'un unique lment. Dans ce cas, le dictionnaire n'a qu'un lment dont la cl sera [modelName] et la valeur associe [modelObject]. les constructeurs [5, 6, 7] sont des variantes des constructeurs [2, 3, 4] dans lesquelles, la vue V n'est plus dsigne par son nom mais directement instancie par un objet implmentant l'interface [View]. L'association nom vue <-> objet View n'a alors plus tre faite dans le fichier de configuration.

Les mthodes de la classe [ModelAndView] sont les suivantes :

1 2

les mthodes [1] et [2] permettent d'ajouter des lments au modle M

3 4

les mthodes [3] et [4] permettent de prciser la vue V soit par une instance View [3], soit par le nom de la vue [4].

Etapes 6-7 : rendu du modle M par la vue V Arriv ici, le flux d'excution a t transfr une vue [View]. [View] est une interface avec l'unique mthode suivante :

assez logiquement, l'unique mthode [render] dispose de l'objet [response] partir duquel la rponse HTTP va tre construite et du modle [model] construit prcdemment par le contrleur. La mthode [render] dispose galement de l'objet [request] qui encapsule la requte HTTP du client. Via cet objet, la mthode [render] a accs au contexte de la requte, la session qui lui est lie, ... 37/70

springmvc - partie1, serge.tahe@istia.univ-angers.fr

L'une des implmentations de l'interface [View] est la classe [JstlView] qui implmente la vue l'aide d'une page JSP utilisant des balises JSTL. La mthode [render] de la classe [JstlView] met les lments du dictionnaire [model] qu'elle a reu dans le contexte de la requte [request]. C'est l que la page JSP qui sera envoye au client trouvera son modle. Spring propose plusieurs implmentations de l'interface [View] :

8.2

Implmenter l'interface View

Mme si Spring offre beaucoup de classes implmentant l'interface View, il est intressant de l'implmenter une fois soi-mme afin de mieux comprendre le mcanisme qui lie le contrleur [Controller] la vue [View] qui il demande d'afficher un modle [Map]. Notre premier exemple Eclipse sera le suivant :

Le fichier [web.xml] du projet est classique :


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-08</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <!-- le mapping des url --> <servlet-mapping> <servlet-name>spring-mvc-08</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>

springmvc - partie1, serge.tahe@istia.univ-angers.fr

38/70

On rappelle que les lignes 8-11 sont ncessaires pour charger la classe charge d'exploiter le fichier [applicationContext.xml] initialisant le contexte de l'application. Le contenu de celui-ci est le suivant :
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> <!-- une liste de personnes --> <bean id="groupe" class="istia.st.springmvc.exemples.web.Groupe"> <property name="membres"> <list> <value>Paul</value> <value>Mlanie</value> <value>Jacques</value> </list> </property> </bean> </beans>

C'est un contenu dj rencontr qui dfinit le bean "groupe" de type [ istia.st.springmvc.exemples.web.Groupe ]. Nous ne revenons pas sur ce fichier dj expliqu. Le fichier [spring-mvc-08-servlet.xml] de dfinition de la servlet [spring-mvc-08] est le suivant :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. <?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="/dosomething.html">DoSomethingController</prop> </props> </property> </bean> <!-- les contrleurs de l'application--> <bean id="DoSomethingController" class="istia.st.springmvc.exemples.web.DoSomething"> <property name="groupe"> <ref bean="groupe"/> </property> </bean> </beans>

L encore, rien de neuf. L'URL [/dosomething.html] sera traite par le contrleur [ istia.st.springmvc.exemples.web.DoSomething ]. Ce contrleur a un champ priv "groupe" qui sera initialis au dmarrage de l'application par le bean "groupe" dfini dans [applicationContext.xml]. Le contrleur [DoSomething] amne un premier changement :
1. package istia.st.springmvc.exemples.web; 2. 3. import java.util.Date; 4. import java.util.HashMap; 5. 6. import javax.servlet.http.HttpServletRequest; 7. import javax.servlet.http.HttpServletResponse; 8. 9. import org.springframework.web.servlet.ModelAndView; 10. import org.springframework.web.servlet.mvc.Controller; 11. 12. public class DoSomething implements Controller { 13. 14. // un groupe de personnes fourni par le contexte de l'application 15. private Groupe groupe; 16. 17. public Groupe getGroupe() { 18. return groupe; 19. } 20. 21. public void setGroupe(Groupe groupe) { 22. this.groupe = groupe; 23. } 24. 25. // gestion de la requte 26. public ModelAndView handleRequest(HttpServletRequest request, 27. HttpServletResponse response) throws Exception { 28. // dbut 29. long dbut = new Date().getTime(); 30. // on fait qq chose... 31. Thread.sleep(10); 32. // fin springmvc - partie1, serge.tahe@istia.univ-angers.fr

39/70

33. 34. 35. 36. 37. 38. 39. 40. 41. 42. } 43. 44. }

long fin = new Date().getTime(); // dure long dure = fin - dbut; // on cre le modle de la vue afficher Map modle=new HashMap(); modle.put("groupe",groupe); modle.put("dure",new Long(dure)); // on retourne le ModelAndView return new ModelAndView(new MyView(),modle);

ligne 12 : [DoSomething] implmente l'interface [Controller] lignes 26-42 : la mthode [handleRequest] de cette interface est dfinie. Jusqu' maintenant, le contrleur avait toujours gnr lui-mme la rponse au client. Grce l'objet [response] qu'elle reoit en paramtre, la mthode [handleRequest] gnrait le flux HTML vers le client et rendait un ModelAndView null [DispatcherServlet] afin d'indiquer que la rponse avait t envoye et que [DispatcherServlet] n'avait plus le faire. Ligne 41, on voit que la mthode rend cette fois une instance de [ModelAndView]. ligne 41 : on utilise le constructeur ModelAndView(View view, Map modle). Le premier paramtre doit tre une instance de classe implmentant l'interface View, le second le modle, sous la forme d'un dictionnaire de type [java.util.Map], que la vue (premier paramtre) devra afficher. lignes 28-35 : on simule un traitement quelconque et on note la dure de celui-ci. ligne 37 : on cre le dictionnaire du modle ligne 38 : on met dedans le champ priv [groupe] du contrleur. On veut montrer que le contrleur a bien accs aux donnes du contexte de l'application (applicationContext.xml). ligne 39 : on place galement dans le modle, la dure du traitement. ligne 41 : on retourne une instance de [ModelAndView], avec pour premier paramtre une instance d'une classe [MyView] que nous allons prsenter, et comme second paramtre, le modle que cette vue devra afficher.

Il ne nous reste plus qu' prsenter la classe [MyView] (dans WEB-INF/src, cf copie d'cran) :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. package istia.st.springmvc.exemples.web; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.View; public class MyView implements View { public void render(Map modle, HttpServletRequest request, HttpServletResponse response) throws Exception { // on code le HTML en dur response.setContentType("text/html"); PrintWriter out = response.getWriter(); // on prpare le code HTML String Html="<html><head><title>Spring-MVC-08</title></head>"+ "<body>"; // on parcourt la liste des membres du groupe Groupe groupe=(Groupe)modle.get("groupe"); ArrayList membres=groupe.getMembres(); for(int i=0;i<membres.size();i++){ Html+=membres.get(i).toString()+"<br>\n"; } // on ajoute la dure d'excution long dure=((Long)modle.get("dure")).longValue(); Html += "<br>DoSomething excut en " + dure + " ms...</body></html>"; // on envoie le flux HTML out.println(Html); }

15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. } 35.

ligne 12 : la classe [myView] implmente l'interface [View]

Nous avons vu au paragraphe 8.1, page 37, que l'interface [View] avait une unique mthode :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

40/70

lignes 14-32 : la mthode [render] de l'interface [View]. Puisqu'on dispose comme paramtre, de l'objet [HttpResponse response], nous sommes capables d'envoyer nous-mmes la rponse au client. C'est ce qui est fait ici. On notera galement que le premier paramtre de la mthode est le modle afficher. ligne 16 : indique au client qu'il va recevoir un flux HTML ligne 17 : on rcupre un canal d'criture ligne 19 : dbut du flux HTML ligne 22 : on rcupre l'objet "groupe" dans le modle lignes 23-26 : on insre son contenu, c.a.d. les membres du groupe, dans le flux HTML lignes 27-29 : on fait de mme avec l'objet "dure" ligne 31 : on envoie le flux HTML au client

Qu'avons-nous fait ? Nous avons simplement dplac l'laboration de la rponse au client, du contrleur vers la vue. Nous avons continu construire le flux HTML la main. Testons. Nous demandons l'url [http://localhost:8080/spring-mvc-08/dosomething.html] :

8.3

L'implmentation JstlView

Dans l'exemple prcdent, nous avons implment l'interface [View] avec une classe propritaire. Dans ce nouvel exemple, l'interface [View] est implmente par une classe prdfinie de Spring MVC, la classe [JstlView] (org.springframework.web.servlet.view.JstlView) :

La classe [JstlView] permet d'afficher un modle [Map] au moyen d'une page JSP utilisant ou non des balises JSTL (Java Standard Tag Library). Cette bibliothque de balises permet de limiter la prsence de code Java dans les pages JSP. La classe [JstlView] a uniquement un constructeur sans paramtres. Elle sert afficher une page JSP qu'il faut prciser au moyen d'une URL. Les mthodes get et set permettant de connatre et de fixer celle-ci sont dfinies dans la classe [AbstractUrlBasedView], une classe parent de [JstlView] :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

41/70

L'exemple Eclipse mettant en oeuvre l'implmentation [JstlView] est le suivant :

L'utilisation de la bibliothque JSTL amne de nouveaux fichiers dans le projet : c.tld : dcrit la syntaxe des balises JSTL. Elle permet aux programmes exploitant ces balises (parseurs) de vrifier leur bonne syntaxe. Nous avons plac ici ce fichier dans [WEB-INF]. En fait, il peut tre en fait un peu n'importe o, sur une machine distante par exemple. standard.jar, jstl.jar : les archives des classes implmentant les balises JSTL. Ces deux archives sont dans le Classpath du projet. Le rpertoire [vues] contient les deux pages JSP [vue0.jsp, vue1.jsp] que l'application va afficher. Ce projet est identique au prcdent. Seuls diffrent le contrleur [DoSomething] et le mcanisme d'affichage des vues. Le contrleur [ DoSomething ] est le suivant :
1. package istia.st.springmvc.exemples.web; 2. 3. import java.util.Date; 4. import java.util.HashMap; 5. 6. import javax.servlet.http.HttpServletRequest; 7. import javax.servlet.http.HttpServletResponse; 8. 9. import org.springframework.web.servlet.ModelAndView; 10. import org.springframework.web.servlet.mvc.Controller; 11. import org.springframework.web.servlet.view.JstlView; 12. 13. public class DoSomething implements Controller { 14. 15. // un groupe de personnes fourni par le contexte de l'application 16. private Groupe groupe; 17. 18. public Groupe getGroupe() { 19. return groupe; 20. } 21. 22. public void setGroupe(Groupe groupe) { 23. this.groupe = groupe; 24. } 25. 26. // gestion de la requte 27. public ModelAndView handleRequest(HttpServletRequest request, 28. HttpServletResponse response) throws Exception { 29. // dbut 30. long dbut = new Date().getTime(); springmvc - partie1, serge.tahe@istia.univ-angers.fr

42/70

31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. } 47. 48. }

// on fait qq chose... Thread.sleep(10); // fin long fin = new Date().getTime(); // dure long dure = fin - dbut; // on cre le modle de la vue afficher Map modle = new HashMap(); modle.put("groupe", groupe); modle.put("duree", new Long(dure)); // on retourne le ModelAndView int i = (int) (Math.random() * 2); JstlView vue = new JstlView(); vue.setUrl("/WEB-INF/vues/vue" + i + ".jsp"); return new ModelAndView(vue, modle);

jusqu' la ligne 40 incluse, on a le mme contrleur que dans l'exemple prcdent ligne 42 : un nombre entier i est gnr alatoirement. Il peut avoir deux valeurs 0 et 1. ligne 43 : on cre une instance de type [JstlView] ligne 44 : on fixe l'URL de la page JSP afficher au moyen de la mthode [setUrl]. Selon la valeur de i, on affichera la page [WEB-INF/vues/vue0.jsp] ou [WEB-INF/vues/vue1.jsp]. ligne 45 : on retourne une instance [ModelAndView] o l'interface [View] est reprsente par l'instance [JstlView] cre et associe une page JSP particulire.

Les pages JSP qui doivent afficher le modle [Map] construit par le contrleur sont les suivantes : [vue0.jsp]
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-09</title> </head> <body> <h2>Vue n&deg; 0</h2> Membres du groupe <table> <c:forEach var="personne" items="${groupe.membres}"> <tr> <td>${personne}</td> </tr> </c:forEach> </table> <br> DoSomething ex&eacute;cut&eacute; en ${duree} ms... </body> </html>

les lignes 1-3 ont t expliques au paragraphe 5, page 18. ligne 10 : affiche le nom de la vue sous la forme Vue n i lignes 13-17 : affichage de l'lment "groupe" du modle. lige 20 : affichage de l'lment "duree" du modle

Rappelons comment sont rcupres les valeurs des lments ${item} d'une page JSP / JSTL.

ligne 20 : la valeur de l'lment ${duree} est cherche successivement dans les contextes suivants : Requte : request.getAttribute(" duree ") Session : session.getAttribute(" duree ") Application : application.getAttribute(" duree ")

Considrons par exemple la page JSP suivante :


1. 2. 3. 4. 5. 6. 7. 8. <%@ taglib uri="/WEB-INF/c.tld" prefix="c" %> <%@ page isELIgnored="false" %> <% String localContext="contexte page"; request.setAttribute("requestContext","contexte request"); session.setAttribute("sessionContext","contexte session"); application.setAttribute("applicationContext","contexte application");

springmvc - partie1, serge.tahe@istia.univ-angers.fr

43/70

9. session.setAttribute("var1","var1 session"); 10. application.setAttribute("var1","var1 application"); 11. %> 12. <html> 13. <head> 14. <title>Contextes d'une page JSP</title> 15. </head> 16. <body> 17. <h3>Contextes d'une page JSP</h3> 18. page : <%=localContext%><br> 19. request : ${requestContext}<br> 20. session : ${sessionContext}<br> 21. application : ${applicationContext}<br> 22. var1 : ${var1}<br> 23. var1 : <%=application.getAttribute("var1")%><br> 24. </body>

25.</html>

ligne 5 : dfinit une variable locale la page ligne 6 : met une donne de cl " requestContext " dans le contexte de la requte. [request] est une variable prdfinie dans une page JSP et dsigne la requte en cours de traitement. ligne 7 : met une donne de cl "sessionContext " dans le contexte de la session. [session] est une variable prdfinie dans une page JSP et dsigne la session laquelle appartient la requte en cours de traitement. ligne 8 : met une donne de cl " applicationContext " dans le contexte de l'application. [application] est une variable prdfinie dans une page JSP et dsigne l'application laquelle appartient la requte en cours de traitement. lignes 9-10 : met une donne de cl " var1 " la fois dans le contexte " session " et le contexte " application " ligne 18 : affiche la variable [localContext] de porte " page " ligne 19 : affiche la variable de cl "requestContext" ligne 20 : affiche la variable de cl "sessionContext" ligne 21 : affiche la variable de cl "applicationContext" ligne 22 : affiche la variable de cl " var1 " de porte " session " ligne 23 : affiche la variable de cl " var1 " de porte "application"

Cette page affiche donne le rsultat suivant :

Expliquons par exemple le processus de traitement de la ligne 21 de la page JSP :


application : ${applicationContext}<br>

La servlet issue de la page JSP recherche une donne de cl " applicationContext " successivement dans les contextes " request ", " session ", " application ". Elle la trouve dans ce dernier contexte. Pour la ligne 22 de la page JSP, la mme processus se droule. La cl "var1 " se trouve la fois dans les contextes " session " et " application ". Elle sera trouve d'abord dans le contexte " session ". Pour obtenir la cl " var1 " de contexte " application " qui est cache par la cl " var1 " de contexte "session", on peut utiliser directement l'objet prdfini [application] comme le montre la ligne 23. Revenons sur la page JSP / JSTL [vue0.jsp] et considrons maintenant la squence suivante :
1. 2. 3. 4. 5. <c:forEach var="personne" items="${groupe.membres}"> <tr> <td>${personne}</td> </tr> </c:forEach>

ligne 1 : la donne ${groupe.membres} est value de la faon suivante : la cl " groupe " est cherche dans les diffrents contextes comme il a t expliqu prcdemment 44/70

springmvc - partie1, serge.tahe@istia.univ-angers.fr

une fois l'objet " groupe " trouv, l'objet [groupe.membres] est obtenu par [groupe.getMembres()] ou [groupe.get(" membres ")]. Le premier cas russira si l'objet " groupe " est un Javabean avec le get appropri pour son champ " membres ". Le second cas russira si l'objet " groupe " est un dictionnaire avec une cl " membres ".

Il nous reste un dernier point expliquer. Comment les lments du modle [Map] construit par le contrleur [DoSomething] sontils arrivs dans un des contextes [" request ", " session ", " application "] ? C'est le contrleur [DispatcherServlet] qui se charge de mettre les lments du dictionnaire [Map] qu'il reoit d'un contrleur [Controller] dans le contexte " request ". La page JSP retrouve donc dans ce contexte tous les lments que le contrleur [Controller] a placs dans le modle [Map]. La page [vue1.jsp] est analogue la vue [vue0.jsp] :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. <%@ 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-09</title> </head> <body> <h2>Vue n&deg; 1</h2> <table border="1"> <tr> <th colspan="3" align="center">Membres du groupe</th> </tr> <tr> <c:forEach var="personne" items="${groupe.membres}"> <td>${personne}</td> </c:forEach> </tr> </table> <br> DoSomething ex&eacute;cut&eacute; en ${duree} ms... </body> </html>

ligne 10 : affichera Vue n 1 ligne 11 : les membres du groupe seront mis en ligne dans un tableau alors que dans la vue n 0, ils sont affichs raison d'un par ligne

Nous sommes prts pour les tests. Nous demandons l'url [http://localhost:8080/spring-mvc-09/dosomething.html]. Premier essai :

En rechargeant plusieurs fois la page (Page reload), on obtient alatoirement les vues 0 ou 1. La vue n 1 est la suivante :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

45/70

8.4

Le rsolveur de noms de vue [InternalResourceViewResolver]

Dans l'exemple prcdent, le contrleur [DoSomething] rendait au contrleur principal [DispatcherServlet] un objet [ModelAndView] construit avec le constructeur suivant :

Ce constructeur exige du contrleur [Controller] qu'il connaisse l'implmentation [View] utiliser pour afficher le modle [Map] qu'il a construit. Cela manque de souplesse. S'il est bien normal que le contrleur [Controller] construise le modle afficher pour l'utilisateur, on ne voit pas pourquoi il dciderait du mode de rendu de celui-ci. Si on est amen changer le mode de rendu (Pdf au lieu de Html), on est oblig de modifier le code. On souhaiterait davantage de souplesse. On peut alors utiliser le constructeur suivant :

Cette fois-ci, le contrleur [Controller] ne prcise que le nom de la vue utiliser. Par configuration, ce nom sera associ une classe implmentant l'interface [View]. L'avantage est bien sr la souplesse apporte par ce mcanisme. Passer d'un rendu Html un rendu Pdf ne ncessitera qu'un changement dans le fichier de configuration, pas dans le code Java. Nous illustrons ce mcanisme avec le projet Eclipse suivant :

De nouveau, l'essence des exemples prcdents est conserve. Le premier changement intervient dans le contrleur [DoSomething] :
springmvc - partie1, serge.tahe@istia.univ-angers.fr

46/70

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.

package istia.st.springmvc.exemples.web; import java.util.Date; import java.util.HashMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class DoSomething implements Controller { // un groupe de personnes fourni par le contexte de l'application private Groupe groupe; public Groupe getGroupe() { return groupe; } public void setGroupe(Groupe groupe) { this.groupe = groupe; } // gestion de la requte public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // dbut long dbut = new Date().getTime(); // on fait qq chose... Thread.sleep(10); // fin long fin = new Date().getTime(); // dure long dure = fin - dbut; // on cre le modle de la vue afficher HashMap modle = new HashMap(); modle.put("groupe", groupe); modle.put("dure", new Long(dure)); // on retourne le ModelAndView int i = (int) (Math.random() * 2); return new ModelAndView("vue"+i, modle); } }

ligne 42 : au lieu de dsigner la vue par une rfrence un type [View], la vue est dsormais dsigne par son nom : vue0 ou vue1 selon la valeur de i.

C'est le fichier de configuration [ spring-mvc-10-servlet.xml] de la servlet [spring-mvc-10] qui va faire le lien entre ce nom et une instance de type [View] :
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. <?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="/dosomething.html">DoSomethingController</prop> </props> </property> </bean> <!-- les contrleurs de l'application--> <bean id="DoSomethingController" class="istia.st.springmvc.exemples.web.DoSomething"> <property name="groupe"> <ref bean="groupe"/> </property> </bean> <!-- le rsolveur de vues --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass"> <value>org.springframework.web.servlet.view.JstlView</value> </property> <property name="prefix"> <value>/WEB-INF/vues/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> </beans>

springmvc - partie1, serge.tahe@istia.univ-angers.fr

47/70

ligne 20 : on dfinit le rsolveur des noms de vues. Ce nom barbare dsigne la classe charge de faire le lien entre le nom d'une vue et la classe charge de son rendu. Un rsolveur de noms de vues implmente l'interface [ViewResolver] :

On voit qu'il y a de nombreuses implmentations disponibles, dont [InternalResourceViewResolver] que nous utilisons dans [spring-mvc-10-servlet.xml]. L'interface [ViewResolver] n'a qu'une mthode :

La mthode [resolveViewName] a pour but d'associer un nom de vue (viewName) une classe implmentant l'interface [View] (rsultat de la mthode). Le second paramtre de la mthode est un objet [Locale] qui identifie la langue et le pays qui seront utiliss par la vue. Par exemple le franais de France mtropolitaine, le franais du Qubec, l'anglais de Grande-Bretagne, l'anglais des USA, ...). C'est gnralement, le navigateur client qui prcise dans quelle [Locale] il veut travailler via un entte HTTP particulier. Nous aurons l'occasion de revenir sur ce point lorsque nous parlerons de l'internationalisation des vues. Le rsolveur des noms de vues [ InternalResourceViewResolver ] utilis dans [spring-mvc-10-servlet.xml] permet de prciser : la classe utiliser pour afficher le modle rendu par le contrleur [Controller] : setViewClass le prfixe avec lequel faire prcder le nom de vue rendu par le contrleur [Controller] pour construire l'url complte de la page JSP : setPrefix le suffixe ajouter au nom de vue rendu par le contrleur [Controller] pour construire l'url complte de la page JSP : setSuffix Les lignes suivantes du fichier [spring-mvc-10-servlet.xml] dfinissent le mode de rsolution des noms de vues :
1. <!-- le rsolveur de vues --> 2. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 3. <property name="viewClass"> 4. <value>org.springframework.web.servlet.view.JstlView</value> 5. </property> 6. <property name="prefix"> 7. <value>/WEB-INF/vues/</value> 8. </property> 9. <property name="suffix"> 10. <value>.jsp</value> 11. </property> 12.</bean>

lignes 3-5 : le modle rendu par le contrleur [Controller] sera affich par une instance de la classe [JstlView] que nous avons tudie dans l'exemple prcdent lignes 6-11 : indique que si le contrleur [Controller] a prcis comme nom de vue, le nom N, la classe [JstlView] devra afficher la page JSP [/WEB-INF/vues/N.jsp], c.a.d. [prfixe/N.suffixe].

Les vues [vue0.jsp, vue1.jsp] sont restes identiques ce qu'elles taient dans l'exemple prcdent, l'attribut HTML <title> prs. Nous sommes prts pour un test. Nous demandons l'url [http://localhost:8080/spring-mvc-10/dosomething.html] :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

48/70

En rechargeant plusieurs fois la page (Page reload), on obtient alatoirement les vues 0 ou 1. La vue n 1 est la suivante :

8.5

Le rsolveur de noms de vue [BeanNameViewResolver]

Revenons sur l'interface [ViewResolver] de rsolution des noms de vue et ses implmentations :

Ci-dessus, on voit qu'une classe d'implmentation est [BeanNameViewResolver]. Le projet suivant illustre l'utilisation de ce rsolveur de noms de vue. Le projet est identique au prcdent. Seul change le rsolveur des noms de vue utilis dans [spring-mvc-11-servlet.xml] :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

49/70

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.

<?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="/dosomething.html">DoSomethingController</prop> </props> </property> </bean> <!-- les contrleurs de l'application--> <bean id="DoSomethingController" class="istia.st.springmvc.exemples.web.DoSomething"> <property name="groupe"> <ref bean="groupe"/> </property> </bean> <!-- le rsolveur de vues --> <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/> <!-- les vues --> <bean id="vue0" class="org.springframework.web.servlet.view.JstlView"> <property name="url"> <value>/WEB-INF/vues/vue0.jsp</value> </property> </bean> <bean id="vue1" class="org.springframework.web.servlet.view.JstlView"> <property name="url"> <value>/WEB-INF/vues/vue1.jsp</value> </property> </bean> </beans>

le rsolveur des noms de vues est dfini lignes 20-31. Le rsolveur [ BeanNameViewResolver ] associe un bean chacun des noms de vue. L'id du bean est le nom de la vue, d'o le nom du rsolveur. Le bean associ au nom d'une vue dfinit : la classe charge du rendu de la vue les paramtres dont cette classe pourrait avoir besoin nous savons que le contrleur [DoSomething] rend des instances [ModelAndView] rfrenant les vues portant les noms : vue0 et vue1. lignes 22-26 : le bean " vue0 " dfinit la vue de nom " vue0 ". La classe charge de rendre cette vue est une instance de [JstlView] (attribut class). L'url de la page JSP que doit afficher l'instance [JstlView] est [/WEB-INF/vues/vue0.jsp] (attribut url). On a ici toutes les informations ncessaires l'affichage de la vue. lignes 27-31 : de faon analogue, le bean " vue1 " dfinit la vue de nom " vue1 ".

Nous pouvons tester. Nous demandons l'url [http://localhost:8080/spring-mvc-11/dosomething.html] :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

50/70

En rechargeant la page, on obtiendra de faon alatoire les vues 0 et 1.

8.6

Le rsolveur de noms de vue [XmlViewResolver]

Revenons sur l'interface [ViewResolver] :

L'une des classes d'implmentation de l'interface [ViewResolver] est, ci-dessus, la classe [XmlViewResolver]. C'est une variante de l'implmentation [BeanNameViewResolver] que nous venons de voir. Une vue de nom N est configure par un bean d'id N. Avec [BeanNameViewResolver], ces beans taient cherchs dans le fichier de configuration [S-servlet.xml] de la servlet S de l'application. Avec [XmlViewResolver], ces beans sont dfinis dans un fichier XML externe. On trouve dans [S-servlet.xml] le nom de ce fichier. Pour illustrer cette stratgie, nous utilisons le projet Eclipse suivant :

Ce projet ne diffre du prcdent que par ses fichiers [spring-mvc-12-servlet.xml] et [vues.xml]. Dans le fichier [spring-mvc-12-servlet.xml], le rsolveur de noms de vue change :
1. 2. 3. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/springbeans.dtd"> <beans>

springmvc - partie1, serge.tahe@istia.univ-angers.fr

51/70

4. <!-- les mappings de l'application--> 5. <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> 6. <property name="mappings"> 7. <props> 8. <prop key="/dosomething.html">DoSomethingController</prop> 9. </props> 10. </property> 11. </bean> 12. <!-- les contrleurs de l'application--> 13. <bean id="DoSomethingController" 14. class="istia.st.springmvc.exemples.web.DoSomething"> 15. <property name="groupe"> 16. <ref bean="groupe"/> 17. </property> 18. </bean> 19. <!-- le rsolveur de vues --> 20. <bean class="org.springframework.web.servlet.view.XmlViewResolver"> 21. <property name="location"> 22. <value>/WEB-INF/vues/vues.xml</value> 23. </property> 24. </bean> 25. </beans>

lignes 20-24 : dfinissent le nouveau rsolveur de noms de vues ligne 20 : la classe d'implmentation [XmlViewResolver] de l'interface [ViewResolver]. Cette classe a un constructeur sans paramtres et une mthode [setLocation] qui permet d'indiquer o se trouve le fichier de dfinition des beans des vues :

lignes 21-23 : dfinissent l'emplacement du fichier XML de dfinition des vues, ici [/WEB-INF/vues/vues.xml]. On peut ne pas dfinir d'emplacement. Dans ce cas, c'est le fichier [/WEB-INF/vues/views.xml] qui est utilis par dfaut.

Dans le fichier [/WEB-INF/vues/vues.xml], on retrouve les beans dj rencontrs avec la stratgie [BeanNameViewResolver] :
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> <bean id="vue0" class="org.springframework.web.servlet.view.JstlView"> <property name="url"> <value>/WEB-INF/vues/vue0.jsp</value> </property> </bean> <bean id="vue1" class="org.springframework.web.servlet.view.JstlView"> <property name="url"> <value>/WEB-INF/vues/vue1.jsp</value> </property> </bean> </beans>

Nous sommes prts pour les tests. Nous demandons l'url [http://localhost:8080/spring-mvc-12/dosomething.html] :

En rechargeant la page, on obtiendra de faon alatoire les vues 0 et 1.

8.7

Le rsolveur de noms de vue [ResourceBundleViewResolver]

Revenons sur l'interface [ViewResolver] :


springmvc - partie1, serge.tahe@istia.univ-angers.fr

52/70

Ci-dessus, l'une des classes d'implmentation de l'interface [ViewResolver] est [ResourceBundleViewResolver]. Comme dans la stratgie [XmlViewResolver] la dfinition des beans des vues est dporte dans un fichier externe, voire des fichiers externes. L'apport de [ResourceBundleViewResolver] vis vis de [XmlViewResolver] est ce qu'on appelle, l'internationalisation des pages. Les vues vont pouvoir tre proposes en diffrentes langues selon, en gnral, la configuration du navigateur client. Montrons sur un exemple simple comment fonctionne ce mcanisme. La configuration de mon navigateur FireFox est la suivante : Tools/ Options / Languages :

Il est configur pour trois langues : le franais (fr), l'anglais amricain (en-us), l'anglais. Lorsque le navigateur fait une demande un site web, ces informations sont envoyes dans un entte HTTP. Le serveur essaie de satisfaire le navigateur dans l'ordre des langues, ici d'abord (fr), puis (en-us), puis (en). Faisons un test. Avec cette configuration, demandons l'url [http://www.google.fr/]

Maintenant changeons la configuration du navigateur comme suit :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

53/70

La langue prfre est donc devenue l'anglais. Demandons la mme url [http://www.google.fr/].

La page nous a t envoye en anglais. La classe [ResourceBundleViewResolver] va nous permettre d'obtenir ce fonctionnement. Cette classe a un constructeur sans paramtres et deux mthodes qui permettent de prciser l'emplacement du fichier (ou des fichiers) de dfinition des beans de vues :

1 2

la mthode [1] permet de dfinir le nom de base du fichier de dfinition des vues. Si ce nom est N, un fichier N_locale.properties sera recherch dans le classpath de l'application web. Le suffixe locale dsigne la langue prfre du navigateur client. Ainsi si celle-ci est l'allemend d'Autriche (de_AT), l'application cherchera un fichier de dfinition des vues appel [N_de_AT.properties], puis le fichier [N_de.properties] et enfin le ficheir [N.properties] dans le classpath de l'application. Ainsi si on a rdig des pages en franais (fr) et en anglais (en), on aura un fichier N_fr.properties pour dfinir les vues en franais un fichier N_en.properties pour dfinir les vues en anglais un fichier N.properties pour dfinir les vues qui seront envoyes aux navigateurs demandant une autre langue, par exemple l'allemand (de). la mthode [2] permet de dfinir plusieurs noms de base plutt qu'un seul. Ainsi si le paramtre de cette mthode est le tableau {" N1 ", "N2 "}, les fichiers N1_locale.properties et N2_locale.properties seront recherchs dans le classpath de l'application. Cela permet de rpartir les dfinitions de vues dans plusieurs fichiers et de faciliter ainsi le travail spar d'quipes de dveloppement.

Pour illustrer la stratgie [ResourceBundleViewResolver], nous utiliserons le projet Eclipse suivant :


springmvc - partie1, serge.tahe@istia.univ-angers.fr

54/70

Le fichier de configuration de la servlet [spring-mvc-13-servlet.xml] 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. <?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="/dosomething.html">DoSomethingController</prop> </props> </property> </bean> <!-- les contrleurs de l'application--> <bean id="DoSomethingController" class="istia.st.springmvc.exemples.web.DoSomething"> <property name="groupe"> <ref bean="groupe"/> </property> </bean> <!-- le rsolveur de vues --> <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> <property name="basename"> <value>vues</value> </property> </bean> </beans>

les lignes 20-25 dfinissent le nouveau rsolveur de noms des vues de type [ResourceBundleViewResolver]. Le nom du fichier des beans des vues est vues (ligne 22). Il y a une valeur par dfaut pour ce fichier : views. Si donc rien n'est prcis, ce sont des fichiers views_locale.properties qui serviront dfinir les vues. Ici, nous n'avons pas utilis la valeur par dfaut et ce seront des fichiers vues_locale.properties qui serviront dfinir les vues. Ils seront recherchs dans le classpath de l'application.

Nous avons plac les fichiers vues_locale.properties dans [WEB-INF/src]. Nous savons qu'Eclipse les recopiera automatiquement dans [WEB-INF/classes] qui fait partie du classpath :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

55/70

vues_fr.properties : dfinit les vues en franais vues_en.properties : dfinit les vues en anglais vues.properties : dfinit les vues pour les autres langues

Examinons le contenu de [vues.properties] :


1. 2. 3. 4. 5. 6. 7. 8. #vue0 vue0.class=org.springframework.web.servlet.view.JstlView vue0.url=/WEB-INF/vues/vue0.jsp #vue1 vue1.class=org.springframework.web.servlet.view.JstlView vue1.url=/WEB-INF/vues/vue1.jsp #vue2 vue2.class=istia.st.springmvc.exemples.web.MyView

le fichier est un fichier de proprits obissant la syntaxe de ce type de fichiers donc contenant des lignes de type attribut=valeur. Ces attributs doivent tre des proprits de la classe. Ainsi utiliser, ligne 3, l'attribut url implique que la classe JstlView ait une mthode [setUrl]. Le lecteur pourra vrifier que c'est bien le cas. Les lignes de [vues.properties] donnent les mmes informations que les beans de la stratgie [XmlViewResolver] de l'exemple prcdent. Ainsi, revenons sur la dfinition de la vue " vue0 " dans la version prcdente :
<bean id="vue0" class="org.springframework.web.servlet.view.JstlView"> <property name="url"> <value>/WEB-INF/vues/vue0.jsp</value> </property> </bean>

1. 2. 3. 4. 5.

les informations ci-dessus sont reprises lignes 1-3 du fichier [vues.properties] lignes 4-6 : dfinissent la vue nomme " vue1 " lignes 7-8 : dfinissent la vue nomme " vue2 ". Pour montrer qu'on n'est pas obligs d'utiliser la mme classe [View] pour toutes les vues, nous proposons d'afficher la vue " vue2 " l'aide de la classe [MyView] dcrite au paragraphe 8.2, page 40. Cette classe ne demande aucun paramtre pour s'initialiser d'o l'absence d'attributs supplmentaires.

Le fichier [vues_fr.properties] est analogue :


1. 2. 3. 4. 5. 6. 7. 8. #vue0 vue0.class=org.springframework.web.servlet.view.JstlView vue0.url=/WEB-INF/vues/vue0_fr.jsp #vue1 vue1.class=org.springframework.web.servlet.view.JstlView vue1.url=/WEB-INF/vues/vue1_fr.jsp #vue2 vue2.class=istia.st.springmvc.exemples.web.MyView

ainsi que le fichier [vues_en.properties] :


1. 2. 3. 4. 5. 6. 7. 8. #vue0 vue0.class=org.springframework.web.servlet.view.JstlView vue0.url=/WEB-INF/vues/vue0_en.jsp #vue1 vue1.class=org.springframework.web.servlet.view.JstlView vue1.url=/WEB-INF/vues/vue1_en.jsp #vue2 vue2.class=istia.st.springmvc.exemples.web.MyView

Les pages JSP affiches par les instances [JstlView] sont les suivantes :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

56/70

les pages suffixes par fr sont celles affiches par [vues_fr.properties] les pages suffixes par en sont celles affiches par [vues_en.properties] les pages sans suffixe sont celles affiches par [vues.properties]

La page [vue0_fr.jsp] est une page en franais :


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-13</title> </head> <body> <h2>Vue n&deg; 0 (fr)</h2> Membres du groupe <table> <c:forEach var="personne" items="${groupe.membres}"> <tr> <td>${personne}</td> </tr> </c:forEach> </table> <br> DoSomething ex&eacute;cut&eacute; en ${duree} ms... </body> </html>

La page [vue0_en.jsp] est analogue mais en anglais :


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-13</title> </head> <body> <h2>View n&deg; 0 (en)</h2> Group members <table> <c:forEach var="personne" items="${groupe.membres}"> <tr> <td>${personne}</td> </tr> </c:forEach> </table> <br> DoSomething executed in ${duree} ms... </body> </html>

La page par dfaut [vue0.jsp] envoye lorsque la langue du navigateur n'est ni le franais (fr), ni l'anglais (en) est la suivante :
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-13</title> </head> <body> <h2>Vue n&deg; 0 (default)</h2> Membres du groupe <table> <c:forEach var="personne" items="${groupe.membres}"> <tr> <td>${personne}</td> </tr> </c:forEach> </table> <br> DoSomething ex&eacute;cut&eacute; en ${duree} ms... </body> </html>

Afin qu'aux tests, on s'y retrouve, la ligne 10 indique (default) pour indiquer que c'est la vue par dfaut qui a t envoye.
springmvc - partie1, serge.tahe@istia.univ-angers.fr

57/70

La mme dmarche a t suivie pour la vue appele " vue1 ". Il ne nous reste plus qu' claircir un mystre. Jusqu' maintenant, le contrleur [DoSomething] demandait l'affichage de deux vues appeles " vue0 " et " vue1 ". On a vu apparatre dans les vues ci-dessus une vue appele " vue2 " qui est rendue par la classe [MyView]. Pour que cette vue soit affiche, le contrleur [DoSomething] a t lgrement modifi :
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. package istia.st.springmvc.exemples.web; import java.util.Date; import java.util.HashMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class DoSomething implements Controller { // un groupe de personnes fourni par le contexte de l'application private Groupe groupe; public Groupe getGroupe() { return groupe; } public void setGroupe(Groupe groupe) { this.groupe = groupe; } // gestion de la requte public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // dbut long dbut = new Date().getTime(); // on fait qq chose... Thread.sleep(10); // fin long fin = new Date().getTime(); // dure long dure = fin - dbut; // on cre le modle de la vue afficher HashMap modle = new HashMap(); modle.put("groupe", groupe); modle.put("duree", new Long(dure)); // on retourne le ModelAndView int i = (int) (Math.random() * 3); return new ModelAndView("vue"+i, modle); } }

ligne 41 : le nombre entier alatoire i prend maintenant ses valeurs dans [0,1,2] au lieu de [0,1] prcdemment. D'o la naissance de la vue appele " vue2 ".

Dans les trois fichiers [vues_locale.properties], la vue nomme " vue2 " est gnre par la classe [MyView]. Nous avons dj expliqu le fonctionnement de cette classe, page 40. Nous ne revenons pas dessus. Elle gnre le mme flux HTML quelquesoit la langue du navigateur :
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.io.PrintWriter; import java.util.ArrayList; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.View; public class MyView implements View { public void render(Map modle, HttpServletRequest request, HttpServletResponse response) throws Exception { // on code le HTML en dur response.setContentType("text/html"); PrintWriter out = response.getWriter(); // on prpare le code HTML String Html="<html><head><title>Spring-MVC-13</title></head>"+ "<body><h2>Vue n 2 (pour toute langue)</h2>"; // on parcourt la liste des membres du groupe Groupe groupe=(Groupe)modle.get("groupe");

springmvc - partie1, serge.tahe@istia.univ-angers.fr

58/70

23. 24. 25. 26. 27. 28. 29. 30. 31. 32. } 33. 34. }

long dure=((Long)modle.get("duree")).longValue(); ArrayList membres=groupe.getMembres(); for(int i=0;i<membres.size();i++){ Html+=membres.get(i).toString()+"<br>\n"; } // on ajoute la dure d'excution Html += "<br>DoSomething excut en " + dure + " ms...</body></html>"; // on envoie le flux HTML out.println(Html);

ligne 20 : on indique par un commentaire (pour toute langue) que la vue ne dpend pas de la langue.

Nous sommes prts pour les tests. Notre navigateur est tout d'abord configur pour demander des pages en franais :

Nous demandons l'url [http://localhost:8080/spring-mvc-13/dosomething.html] :

En rechargeant la page (page reload), nous finissons par avoir les deux autres vues : La vue " vue2 " gnre par la classe [MyView] :

et la vue " vue1 " :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

59/70

Nous configurons notre navigateur pour qu'il travaille maintenant en anglais :

Nous redemandons l'url [http://localhost:8080/spring-mvc-13/dosomething.html] :

Nous avons obtenu la version anglaise de la vue " vue0 ". Nous rechargeons la page autant de fois que ncessaire pour avoir les deux autres vues :

La vue n 2 ne change pas car elle est indpendante de la langue.

springmvc - partie1, serge.tahe@istia.univ-angers.fr

60/70

On obtient ci-dessus la version anglaise de la vue " vue1 ". Changeons une nouvelle fois la langue prfre du navigateur pour choisir l'allemand :

Recommenons le processus prcdent. La vue n 2 reste identique :

La vue n 1 obtenue est la suivante :

Ceci est anormal. En effet, le fichier [vues_de.properties] n'tant pas dfini, Spring MVC aurait du utiliser le fichier par dfaut [vues.properties]. Ce n'est pas le cas. C'est le fichier [vues_fr.properties] qui a t utilis. Aprs de multiples vrifications, je n'ai pas trouv l'explication de ce problme. On peut vrifier que l'objet [Locale] associ la requte est le bon (de). Il y a diverses faons de vrifier ce point. Une faon simple est de crer le fichier [vues_de.properties]. On constate qu'il est alors utilis.
springmvc - partie1, serge.tahe@istia.univ-angers.fr

61/70

Pour l'instant, je conclus que je suis pass ct de quelque chose dans le fonctionnement du rsolveur de noms de vues [ResourceBundleViewResolver]. Cela fera l'objet d'un " Errata " si je trouve l'explication.

8.8

Rediriger le navigateur client

Revenons sur l'interface [View] que doivent implmenter les classes charges d'envoyer une vue au client :

Ci-dessus, on voit qu'il existe une classe d'implmentation appele [RedirectView]. Elle permet d'envoyer en rponse au client, un ordre de redirection vers une URL donne :

Le dernier constructeur donne les trois paramtres importants pour la redirection :


url : url vers laquelle doit se rediriger le navigateur client contextRelative (defaut=false) : indique si l'url prcdente est relative ou non au contexte de l'application. Si oui, l'url de redirection sera prfixe par le contexte (=nom) de l'application. Ce paramtre n'intervient que si l'url commence par /. Si url=/URL, l'url de redirection sera /C/URL o C est le contexte de l'application si contextRelative=true, et simplement /URL si contextRelative=false. http10Compatible (defaut=true) : indique si la redirection doit tre comptaible avec le protocole HTTP 1.0. Avec le protocole HTTP 1.1, le code HTTP de redirection est 303 alors qu'avec HTTP 1.0 c'est le code 302. Comme beaucoup de clients HTTP 1.1 traitent le code 302 comme le code 303, il peut tre intressant d'envoyer le code 302 qui conviendra la fois aux clients HTTP 1.0 et 1.1. C'est ce qui se passe avec http10Compatible=true.

Pour illustrer la redirection, nous utiliserons le projet Eclipse suivant :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

62/70

Ce projet reprend l'essentiel du projet prcdent en liminant toutefois l'aspect internationalisation. On a les mmes vues quelque soit la langue prfre du navigateur. Comme prcdemment le rsolveur de noms de vues est [ResourceBundleViewResolver] et la dfinition des vues est faite dans [vues.properties] :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. #vue0 vue0.class=org.springframework.web.servlet.view.RedirectView vue0.url=/jsp/redirect.jsp vue0.contextRelative=true vue0.http10Compatible=false #vue1 vue1.class=org.springframework.web.servlet.view.JstlView vue1.url=/WEB-INF/vues/vue1.jsp #vue2 vue2.class=istia.st.springmvc.exemples.web.MyView

Comme prcdemment, il y aura trois vues : " vue0 ", " vue1 ", " vue2 ". Celles-ci sont dfinies ci-dessus :

lignes 1-5 : dfinissent la vue " vue0 " ligne 2 : la classe d'affichage de la vue est [RedirectView]. La vue " vue0 " est donc une demande de redirection. ligne 3 : l'url de redirection ligne 4 : l'url prcdente est relative au contexte. Le navigateur client sera donc redirig vers l'url /spring-mvc14/jsp/redirect.jsp. On peut voir plus haut, sur la copie d'cran, cette page JSP. On se rappelle qu'une vue sert afficher un modle [Map] construit par un contrleur [Controller]. Que devient ce modle [Map] dans le cas d'une redirection ? Il va tre ajout l'Url de redirection. Ainsi si le modle [Map] possde les lments [cle1,valeur1] [cle2,valeur2], l'url de redirection aura la forme [/urlRedirection?cle1=valeur1&cle2=valeur2]. Comme clei et valeuri sont des objets, ce sont en ralit leurs mthodes [toString] qui sont utilises pour crer la chane des paramtres de la requte. Nous en verrons un exemple bientt. ligne 5 : la redirection sera compatible HTTP 1.0 lignes 6-8 : dfinissent la vue " vue1 ". Elle sera affiche par une instance [JstlView]. lignes 9-10 : dfinissent la vue " vue2 ". Elle sera affiche par une instance [MyView].

Comme prcdemment, le contrleur [DoSomething] demande de faon alatoire l'affichage de ces trois vues. La vue " vue0 " demande la redirection du client vers la vue [redirect.jsp] suivante :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. <%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%> <% String duree=request.getParameter("duree"); %> <html> <head> <title>Spring-mvc-14</title> </head> <body> <h2>Vue n&deg; 0 obtenue par redirection (pour toute langue)</h2> <br>

springmvc - partie1, serge.tahe@istia.univ-angers.fr

63/70

12. DoSomething ex&eacute;cut&eacute; en <%=duree%> ms... 13. </body> 14. </html>

Le contrleur [DoSomething] demande toutes les vues d'afficher un modle [Map] avec les lments suivants : la dure de l'excution sous la forme d'un entier long [Long] associ la cl " duree " le groupe de personnes sous la forme d'un objet [Groupe] associ la cl " groupe ". L'url de redirection aura alors la forme suivante :
http://localhost:8080/spring-mvc-14/jsp/redirect.jsp?groupe=istia.st.springmvc.exemples.web.Groupe%40623367&duree=10

On y retrouve : l'url de redirection : /spring-mvc-14/jsp/redirect.jsp la chane des paramtres qui reprsente le modle construit par le contrleur [DoSomething] : groupe=istia.st.springmvc.exemples.web.Groupe%40623367&duree=10 La cl " duree " tant associe un type [Long], la mthode [toString] de cette classe a t utilise et on obtient le 10 de [duree=10]. La cl " groupe " est, elle, associe un objet de type [Groupe] pour lequel la mthode [toString] n'avait pas t redfinie. Aussi c'est la mthode [toString] de la classe [Object] qui a t utilise pour introduire la valeur associe la cl " groupe " dans la chane des paramtres. En redfinissant une mthode [toString] pour la classe [Groupe], on aurait pu faire en sorte que la valeur soit affiche, par exemple, sous la forme Paul,Mlanie,Jacques. Le seul paramtre exploitable de la chane de paramtres tant le paramtre " duree ", la valeur de celui-ci est rcupre ligne 2 de la page [redirect.jsp] et affiche ligne 12. La vue " vue1 " est la mme que dans les exemples prcdents, de mme que la vue " vue2 " gnre par [MyView]. Nous sommes prts pour les tests. Nous demandons l'url [http://localhost:8080/spring-mvc-14/dosomething.html] :

Nous avons obtenu la vue " vue1 ". En rechargeant la page (Page reload) plusieurs fois, on obtient galement les vues " vue0 " et " vue2 " :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

64/70

La vue " vue0 " a provoqu une redirection vers l'adresse affiche ci-dessus par le navigateur.

8.9

Diriger une vue vers une ressource interne

Revenons sur l'interface [View] que doivent implmenter les classes charges d'envoyer une vue au client :

Ci-dessus, on voit qu'il existe une classe d'implmentation appele [InternalResourceView]. Elle permet de rediriger le modle construit par un contrleur [Controller] vers un autre contrleur plutt que vers une vue. Pour illustrer cette possibilit, nous utiliserons le projet Eclipse suivant :

La configuration [spring-mvc-15-servlet.xml] de la servlet est analogue celle des deux projets prcdents :
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="/dosomething.html">DoSomethingController</prop> 9. <prop key="/dosomethingelse.html">DoSomethingElseController</prop> 10. </props> 11. </property> 12. </bean> springmvc - partie1, serge.tahe@istia.univ-angers.fr

65/70

13. <!-- les contrleurs de l'application--> 14. <bean id="DoSomethingController" 15. class="istia.st.springmvc.exemples.web.DoSomething"> 16. <property name="groupe"> 17. <ref bean="groupe"/> 18. </property> 19. </bean> 20. <bean id="DoSomethingElseController" 21. class="istia.st.springmvc.exemples.web.DoSomethingElse"> 22. </bean> 23. <!-- le rsolveur de vues --> 24. <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> 25. <property name="basename"> 26. <value>vues</value> 27. </property> 28. </bean> 29. </beans>

La nouveaut vient du fait que nous ajoutons un second contrleur [Controller] :


ligne 9 : l'url [/dosomethingelse.html] est associe au contrleur d'id [DoSomethingElseController] lignes 20-22 : dfinissent le contrleur d'id [DoSomethingElseController] ligne 21 : la classe d'implmentation est de type [istia.st.springmvc.exemples.web.DoSomethingElse] une classe dfinie dans [WEB-INF/src] (cf copie d'cran).

Pour comprendre le rle du contrleur [DoSomethingElse], il nous faut tout d'abord parler des vues dfinies dans [vues.properties] :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. #vue0 vue0.class=org.springframework.web.servlet.view.InternalResourceView vue0.url=/dosomethingelse.html #vue1 vue1.class=org.springframework.web.servlet.view.JstlView vue1.url=/WEB-INF/vues/vue1.jsp #vue2 vue2.class=istia.st.springmvc.exemples.web.MyView #vue3 vue3.class=org.springframework.web.servlet.view.JstlView vue3.url=/WEB-INF/vues/vue3.jsp

lignes 1-3 : dfinissent la vue " vue0 " ligne 2 : la classe implmentant l'interface [View] est de type [InternalResourceView] ligne 3 : la ressource qui doit tre instancie pour traiter le modle associ la vue " vue0 ". Cette ressource est ici donne sous la forme d'une url. La stratgie de rsolution des url est utilise pour trouver la ressource associe. La ligne 9 du fichier [spring-mvc-15-servlet.xml] indique que cette ressource est le contrleur d'id [DoSomethingElseController]. Il s'agit donc d'une instance de la classe [istia.st.springmvc.exemples.web.DoSomethingElse]. Lorsque le contrleur [DoSomething] va demander la vue " vue0 " d'afficher le modle [Map] qu'il a construit : les lments du modle [Map] seront mis un un dans le contexte de la requte le contrleur [DoSomethingElse] sera instanci. Il trouvera dans le contexte de la requte, le modle [Map] construit par le contrleur [DoSomething]. Il pourra alors travailler avec, l'enrichir, construire un nouveau modle et demander son tour l'affichage d'une vue. On a donc, dans cet exemple, un chanage de contrleurs. La ressource interne n'est pas ncessairement un objet de type [Controller]. Ce peut tre une servlet quelconque capable d'exploiter le contexte de la requte. lignes 4-6 : la vue " vue1 " telle qu'on la connat dj lignes 7-8 : la vue " vue2 " telle qu'on la connat dj lignes 9-11 : la vue " vue3 " qui sera associe la page JSP [vue3.jsp]. C'est le contrleur [DoSomethingElse] qui demande l'affichage de cette vue. Nous y reviendrons.

Le contrleur [DoSomething] n'a pas chang :


1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. package istia.st.springmvc.exemples.web; import java.util.Date; import java.util.HashMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class DoSomething implements Controller { // un groupe de personnes fourni par le contexte de l'application private Groupe groupe;

springmvc - partie1, serge.tahe@istia.univ-angers.fr

66/70

17. public Groupe getGroupe() { 18. return groupe; 19. } 20. 21. public void setGroupe(Groupe groupe) { 22. this.groupe = groupe; 23. } 24. 25. // gestion de la requte 26. public ModelAndView handleRequest(HttpServletRequest request, 27. HttpServletResponse response) throws Exception { 28. // dbut 29. long dbut = new Date().getTime(); 30. // on fait qq chose... 31. Thread.sleep(10); 32. // fin 33. long fin = new Date().getTime(); 34. // dure 35. long dure = fin - dbut; 36. // on cre le modle de la vue afficher 37. HashMap modle = new HashMap(); 38. modle.put("groupe", groupe); 39. modle.put("duree", new Long(dure)); 40. // on retourne le ModelAndView 41. int i = (int) (Math.random() * 3); 42. return new ModelAndView("vue"+i, modle); 43. } 44. 45. }

Les vues demandes par [DoSomething] sont " vue0 ", " vue1 " et " vue2 ". Les vues " vue1 " et " vue2 " vont tre traites comme dans l'exemple prcdent. La vue " vue0 " ne provoque, elle, pas d'affichage de vue au client mais le passage du flux d'excution au contrleur [DoSomethingElse]. Celui-ci 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. package istia.st.springmvc.exemples.web; import java.util.Date; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class DoSomethingElse implements Controller { // gestion de la requte public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // dbut long dbut = new Date().getTime(); // on fait qq chose... Thread.sleep(20); // fin long fin = new Date().getTime(); // dure long dure = fin - dbut; // on met la dure dans le contexte de la requte request.setAttribute("duree2",new Long(dure)); // on retourne le ModelAndView return new ModelAndView("vue3", null); } }

ligne 13 : la mthode [handleRequest] de [DoSomethingElse] va tre appele par le contrleur principal [DispatcherServlet]. Elle reoit en paramtres les mmes objets [request, response] que le contrleur [DoSomething] les lments du modle [Map] cr par le contrleur [DoSomething] ont t placs par [DispatcherServlet] dans le contexte de la requte. Ainsi doit-on trouver dans ce contexte les cls " duree " et " groupe " du modle [Map] cr par le contrleur [DoSomething]. lignes 15-22 : on calcule la dure de l'excution ligne 24 : qui est mise ensuite dans le contexte de la requte associe la cl " duree2 " ligne 26 : on retourne un [ModelAndView] tel que la vue est " vue3 " et le modle est vide. Ce qu'on veut montrer ici, c'est que le modle n'est pas obligatoire. Si on est certain par exemple que la vue va tre affiche par la classe [JstlView], le contexte de la requte peut servir de modle. Nanmoins, cette mthode est dconseiller car elle fait perdre de la souplesse l'application.

La vue " vue3 " est dans [vues.properties] associe la page JSP [vue3.jsp]. Celle-ci est la suivante :
1. 2. <%@ page language="java" pageEncoding="ISO-8859-1" contentType="text/html;charset=ISO-8859-1"%> <%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>

springmvc - partie1, serge.tahe@istia.univ-angers.fr

67/70

3. <%@ page isELIgnored="false" %> 4. 5. <html> 6. <head> 7. <title>Spring-mvc-15</title> 8. </head> 9. <body> 10. <h2>Vue n&deg; 3 (pour toute langue)</h2> 11. <table border="1"> 12. <tr> 13. <th colspan="3" align="center">Membres du groupe</th> 14. </tr> 15. <tr> 16. <c:forEach var="personne" items="${groupe.membres}"> 17. <td>${personne}</td> 18. </c:forEach> 19. </tr> 20. </table> 21. <br> 22. DoSomething ex&eacute;cut&eacute; en ${duree} ms...<br> 23. DoSomethingElse ex&eacute;cut&eacute; en ${duree2} ms...<br> 24. </body> 25. </html>

la page JSP affiche les lments de cl " groupe " (ligne 16), " duree " (ligne 22) et " duree2 " (ligne 23). les lments de cl " groupe " (ligne 16) et " duree " (ligne 22) ont t tout d'abord placs dans le modle [Map] cr par le contrleur [DoSomething]. Lors du passage de tmoin entre [DoSomething] et [DoSomethingElse], le contrleur [DispatcherServlet] a plac les lments du modle dans le contexte de la requte. C'est pourquoi la page JSP prsente peut les rcuprer. la cl " duree2 " a elle t place explicitement dans le contexte de la requte par le contrleur [DoSomethingElse].

Nous sommes prts pour des tests. Nous demandons l'url [http://localhost:8080/spring-mvc-15/dosomething.html] :

Nous avons ici obtenu la vue " vue3 ". En rechargeant plusieurs fois la page, on obtient les deux autres vues :

springmvc - partie1, serge.tahe@istia.univ-angers.fr

68/70

9 Conclusion de la partie 1
Rappelons l'architecture Spring MVC que nous avons tudie dans ce document : Couche Interface Utilisateur[ui] utilisateur
1

DispatcherServlet
2 3 4

Couche mtier [metier]

Couche d'accs aux donnes [dao]

Donnes

Controller
5

Modle Map
6

View
7

Nous avons vu les points suivants :


les diffrentes stratgie 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 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] les diffrentes stratgie 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]

Il nous reste

travailler sur les implmentations de l'interface [Controller] que met notre disposition Spring MVC. L'une d'elles, appele [SimpleFormController], nous permet d'crire des contrleurs de formulaire HTML avec contrle de validit des donnes, d'une manire analogue celle utilise dans le framework Struts. prsenter une application web MVC 3tier un peu raliste prsenter divers complments tels que par exemple les vues Excel ou PDF

springmvc - partie1, serge.tahe@istia.univ-angers.fr

69/70

Table des matires


1GNRALITS........................................................................................................................................................................... 2 2OUTILS UTILISS ET RFRENCES................................................................................................................................... 4 3LE CODE DES EXEMPLES...................................................................................................................................................... 4 4LES COMPOSANTES D'UNE ARCHITECTURE SPRING MVC....................................................................................... 9 5CONFIGURATION D'UNE APPLICATION SPRING MVC.............................................................................................. 13 6LA LIAISON URL - CONTROLLER..................................................................................................................................... 18 6.1LES STRATGIES DE RSOLUTION DES URL....................................................................................................................................18 6.2LA STRATGIE SIMPLEURLHANDLERMAPPING...............................................................................................................................19 6.3LA STRATGIE BEANNAMEURLHANDLERMAPPING........................................................................................................................ 21 6.4LA STRATGIE SIMPLEURLHANDLERMAPPING AVEC LIAISONS EXTERNALISES................................................................................. 23 7ACCS AU CONTEXTE D'UNE APPLICATION SPRING MVC..................................................................................... 24 7.1EXEMPLE 1................................................................................................................................................................................. 24 7.2EXEMPLE 2................................................................................................................................................................................. 28 7.3EXEMPLE 3................................................................................................................................................................................. 31 7.4EXEMPLE 4................................................................................................................................................................................. 33 8GESTION DES VUES............................................................................................................................................................... 35 8.1LA PLACE DES VUES DANS L'ARCHITECTURE SPRING MVC............................................................................................................. 35 8.2IMPLMENTER L'INTERFACE VIEW.................................................................................................................................................38 8.3L'IMPLMENTATION JSTLVIEW..................................................................................................................................................... 41 8.4LE RSOLVEUR DE NOMS DE VUE [INTERNALRESOURCEVIEWRESOLVER]..........................................................................................46 8.5LE RSOLVEUR DE NOMS DE VUE [BEANNAMEVIEWRESOLVER].......................................................................................................49 8.6LE RSOLVEUR DE NOMS DE VUE [XMLVIEWRESOLVER].................................................................................................................51 8.7LE RSOLVEUR DE NOMS DE VUE [RESOURCEBUNDLEVIEWRESOLVER]............................................................................................ 52 8.8REDIRIGER LE NAVIGATEUR CLIENT................................................................................................................................................62 8.9DIRIGER UNE VUE VERS UNE RESSOURCE INTERNE............................................................................................................................65 9CONCLUSION DE LA PARTIE 1.......................................................................................................................................... 69

springmvc - partie1, serge.tahe@istia.univ-angers.fr

70/70