SpringMVC v3 pour les nuls avec une démo détaillée

L'objectif est de montrer avec détail comment prendre en main le framework SpringMVC v3.

Cet article s'adresse aux nuls avec comme seul pré-requis de connaître (un peu) le framework Spring.

SpringMVC, dans sa dernière version, offre d'énormes simplifications si on sait l'exploiter.

Il apporte une réponse élégante pour bon nombre de problématiques réelles.

Le projet web démo est basé sur maven3 et java 5+ et toujours avec "full annotations".

Il commence par créer le projet Maven à partir d'une template.

Le pom.xml est configuré pour utiliser tomcat ou jetty embarqués.

Dans la première partie, une classe 'Controller' de traitement et une page jsp sont écrites afin d'afficher le fameux 'Hello World'.

Dans la seconde partie, la fonctionnalité standard de création d'une entité métier est rajoutée.

Pour cela, on écrit un 'Controller' qui affiche le formulaire de saisie et un autre qui gère les données saisies et soumises.

Passons à la mise en pratique.

PREMIÈRE PARTIE: Juste un hello world

Voici les étapes de la première partie de la démo.

ETAPE 1. Créer le projet maven

Dans le workspace de votre eclipse, lancer la console dos puis taper la commande:

mvn archetype:generate -Dfilter=maven-archetype-webapp

La commande permet de créer interactivement le projet web maven et il suffit de répondre aux questions posées.

Un nouveau répertoire est créé dans le workspace.

Placer vous dans ce répertoire et lancer la commande:

mvn eclipse:eclipse

Enfin, importer le projet dans eclipse.

ETAPE 2. Configurer les dépendances maven.

Télécharger le fichier pom.xml ici. Recopier le contenu (uniquement les blocs properties, dépendencies & build) dans le pom de votre projet.

ETAPE 3. Configurer le projet web.

Comme notre démo est une application web, configurons son fichier web.xml :

 <?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
 <servlet>
  <servlet-name>dispatcher</servlet-name>
  <servlet-class>
    org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
	<load-on-startup>2</load-on-startup>
         <init-param>
	  <param-name>contextConfigLocation</param-name>
	  <param-value>/WEB-INF/applicationContext.xml</param-value>
		</init-param>
  </servlet>
  <servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>*.html</url-pattern>
  </servlet-mapping>

Les points importants dans le web.xml sont:

a- Le header du web.xml mentionne la version de la servlet utilisée 2.5 ( .../web-app_2_5.xsd ).

b- La seule servlet DispactherServlet enregistrée est nommée "dispatcher". C'est le point d'entrée des requêtes (cf. point d ci-dessous).

c- La servlet SpringMVC "dispatcher" fait office de 'controller' frontal (de façade).

d- Le mapping indique que toute URL avec extension .html sera interceptée par la servlet "dispatcher".

e- Le paramètre init-param fait pointer ContextConfigLocation sur le fichier de spring (nommé applicationContext.xml).

Ce paramètre devient optionnel si on respecte le principe "convention over configuration" en nommant le fichier de spring "dispacher-context.xml".

Explications:

Le contenu du fichier web.xml signifie que:

Toutes les requêtes d'extension.html sont interceptées par le 'controller' frontal SpringMVC (servlet dispatcher).

Le 'controller' frontal comme son nom l'indique ne fait aucun traitement.

Il ne fait que déléguer (dispatcher) à d'autres 'controller' de traitements métier (voir paragraphe suivant).

A l'arrivée d'une requête http, le 'controller' frontal détermine selon une stratégie (par défaut ou à définir) quelle méthode appelée et quelle vue renvoyée.

Exemple:

Supposons qu'une requête /HOST:PORT/CONTEXT_WEB_PROJ/hello.html est reçue.

Puisque l'extension .html est présente, le dispatcher analyse l'URL pour identifier le 'controller' de traitement.

L'annotation @Controller permet au dispatcher d'identifier la classe 'controller' de traitement de la requête.

ETAPE 4. Le controller de traitement

Notre premier 'controller' est simple presque minimaliste pour faciliter la prise en main de Spring MVC3.

package fr.netapsys.mvc3;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class PersonController {

	@RequestMapping("/hello")
	public  String salut(Model model){
	   model.addAttribute("hello","Hello world spring mvc3");
	   return "WEB-INF/jsp/hello.jsp";
	}
 }

Découvrons les concepts clés qui sont:

a) La classe est annotée avec @Controller, c'est elle le 'controller' de traitement.

L'annotation @Controller est une spécialisation de @Component qui sera auto-détectée et injectée par le conteneur Spring.

Voir ci-après le fichier de config de Spring.

La méthode "salut" annotée avec @RequestMapping("/hello") montre que c'est cette méthode qui traite l'URI "/hello.html".

Cette méthode ajoute un attribut ayant le scope request et nommé "hello" dans l'objet Model (Map).

Puis elle retourne la chaîne "WEB-INF/views/hello.jsp" qui indique explicitement la page jsp qui se charge de présenter la vue (réponse).

C'est une page JSP codée en dur . Ceci est très mauvais puisque il couple le code java avec la vue!

Dans la seconde partie, on améliore ce point en configurant dans spring la stratégie de présentation (vue).

ETAPE 5. Configurer Spring

Le fichier de spring nommé applicationContext.xml comme mentionné dans le web.xml localisé dans /WEB-INF/ contient peu de lignes:

<beans 
xmlns="http://www.springframework.org/schema/beans" 
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" 
xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

<context:component-scan base-package="fr.netapsys.mvc3" />
</beans>

Ce fichier contient une seule ligne pour l'instant.
Ceci ordonne au conteneur spring de scanner le package (et ses sous packages) pour charger les classes annotées.

ETAPE 6. Page JSP de la présentation

La page jsp se nomme hello.jsp localisée dans WEB-INF/jsp.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>HELLO Page</title>
</head>
<body>
<c:out value="${hello}"/>
</body>
</html>

Deux points importants méritent d'être détaillés:

a) La présence dans l'entête du jsp de la directive :

 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

Noter la présence de jsp dans le namespace .../jsp/jstl/core.
La version 1.2 de jstl doit être inclus dans le pom du projet. Le web.xml doit pointer sur la servlet 2.5+.

b) La taglib jstl permet d'afficher l'attribut hello (l'attribut 'hello' de l'étape 4).

ETAPE 7. Tester l'application web

Lancer sur la console dos la commande :

mvn tomcat:run

ou

mvn jetty:run

Saisir dans le browser l'url http://localhost:8080/NOM_CONTEXT/hello.html après avoir remplacé le NOM_CONTEXT par le nom de votre projet (context web).

Le message "Hello world spring mvc3" doit s'afficher.

Résumé de la première partie:

Cette première partie a permis d'expliquer comment configurer le 'controller' frontal ou de façade nommé "dispacher" de SpringMVC.

A l'uri /hello.html envoyée par le navigateur, le 'dispatcher' identifie via l'annotation @Controller la classe qui va gérer la requête puis via l'annotation @RequestMapping la méthode à exécuter.

C'est cette méthode qui indique via une chaîne renvoyée la vue à retourner à l'utilisateur.

C'est pourquoi un premier 'controller' de traitement a été écrit. Et la vue est matérialisée par une page jsp nommé 'hello.jsp'.

Noter que le retour de la méthode "salut" est, codé en dur, "/WEB-INF/jsp/hello.jsp" détermine la vue à retourner.

Ce dernier point est un défaut de conception (et de sécurité). La seconde partie le corrige.

SECONDE PARTIE: Gestion d'un formulaire de création

D'abord on doit comprendre la cinématique de création d'une nouvelle entité. Dans notre cas, il s'agit de créer une nouvelle personne:

L'utilisateur clique sur un lien de création ou saisit une URL pour voir un formulaire vide s'afficher lui permettant de saisir les données attendues.

Le clic sur ce lien correspond à une requête http de type GET.

Pour cela un premier 'controller' est écrit pour traiter cette requête GET et une page jsp est associée.
Lorsque l'utilisateur complète le formulaire et confirme via le bouton 'submit', un second controller doit se charger de traiter le formulaire soumis et diriger vers une page jsp la réponse à l'utilisateur.

En réalité, c'est un seul 'controller' et c'est toujours la classe PersonController avec les deux méthodes de gestion:
- de l'affichage du formulaire,
- de traitement de la soumission des données (méthode de type POST).

Voici le tableau de correspondances entre les URI saisies, les méthodes exécutées et les pages jsp présentées.

blog_mvc3.PNG

Mais avant cela, on va corriger le défaut de conception de la première partie en configurant dans Spring la stratégie de choisir la vue.

ETAPE 1: Compléter le fichier de config de spring

Ajouter dans le fichier applicationContext.xml de la première partie ce bout de code xml:

<bean id="viewResolver"
 class="org.springframework.web.servlet.view.UrlBasedViewResolver">
  <property name="viewClass"
      value="org.springframework.web.servlet.view.JstlView" />
  <property name="prefix" value="/WEB-INF/jsp/" />
  <property name="suffix" value=".jsp" />
</bean>

Explications:

On laisse Spring utiliser la stratégie 'UrlBasedViewResolver' pour déterminer à l'aide de la chaîne renvoyée par la méthode gérant la requête quelle vue retournée.
La stratégie 'UrlBasedViewResolver' se base sur la chaîne retournée en la préfixant et suffixant des éléments indiqués dans la configuration.
Par exemple, si la méthode retourne la chaîne 'view' alors la vue sera "/WEB-INF/jsp/view.jsp".

ETAPE 2: Adapter la classe PersonController

Maintenant qu'une stratégie de la vue est déclarée, on doit rectifier la méthode 'salut' de la classe PersonController comme suit:

@RequestMapping("/hello")
  public  String salut(Model model){
	model.addAttribute("hello","Hello world spring mvc3");
 	return "hello";
  }

Elle retourne désormais une chaîne "hello" au lieu "/WEB-INF/jsp/hello.jsp" de la première partie.

Ainsi pour tout le reste, les méthodes retourneront une chaîne.

ETAPE 3: Classe POJO (DTO)

La classe POJO PersonForm va être utilisée dans cette partie. Voici son code simple sans les getters/setters.

public class PersonForm implements Serializable{
 private static final long serialVersionUID = 1L;
 private String name;
  private String dateBirth;
  @Override
  public String toString() {
   return "PersonForm [name=" + name + 
               ", dateBirth=" + dateBirth + "]";
  }
}

Les propriétés sont toutes volontairement des String.

ETAPE 4: Affichage du formulaire de saisie

Ceci correspond donc à l'uri /person.

ETAPE 4.1: Page jsp d'affichage

Pour cela, la page jsp view.jsp est nécessaire. Voici son code :

<%@page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Formulaire de saisie</title>
</head>
<body>
<br/>
<div><br/>
<form:errors cssClass="errors"/>
 <form:form  modelAttribute="person" action="create.html">
 <label for="name">Nom:</label>
 <form:input path="name"  maxlength="30"  size="30"/><br/><br/>
<label for="name">Date naissance:</label>
<form:input path="dateBirth" />
<input type="submit" id="ok" value="OK" />
</form:form> 
</div></body>
</html>

Explications:

Dans cette page jsp, nous recourons aux taglibs de Spring. D'où les inclusions de l'entête.

L'action lors de la soumission du formulaire est create.html comme l'indique le tag <form:form.

La prorpiété path du tag <form:input permet de faire le lien entre une propriété du POJO PersonForm du model et le champ de saisie.

ETAPE 4.2: Controller d'affichage

Ajoutons au controller PersonController de la première partie la méthode display suivante:

@RequestMapping("/person")
public  String display(@ModelAttribute("person") PersonForm pf){
   return "view";
}

L'annotation @RequestMapping de la méthode assure que c'est elle qui traite l'URI /person.

Vous constatez que la méthode display retourne une chaîne 'view'.

La méthode display prend en argument un objet de type PersonForm annotée avec @ModelAttribute.

Ceci indique à SpringMVC que l'objet PersonForm est un élément du model et qu'un objet vide doit exister dans le scope request.

Vous constatez aussi que, dans la page view.jsp, le tag <form:form utilise une propriété modelAttribute fixée à 'person'.

SpringMVC assure via ce tag la correspodance entre l'attribut du Model et les éléments du formulaire de la page jsp.

ETAPE 5: Test de l'application

Relancer la commande :

mvn tomcat:run

Puis saisir l'url localhost:8080/NOM_CONTEXT/person.html; vous verrez alors le formulaire suivant s'afficher:

blog_mvc3_formulaire_saisi.PNG

Si jusque là tout va bien, il nous reste à écrire le bout de code java pour traiter le clic sur le bouton OK.

C'est l'objet de l'étape suivante.

ETAPE 6: Traitement des données saisies

La classe controller va être complétée avec la méthode validée comme suit:

@RequestMapping(value="/create", method=RequestMethod.POST)
public String valider(@ModelAttribute("person") PersonForm pf,
                             BindingResult result, Model mp){
  /* convert personForm and persist it with JPA */
  //mp.addAttribute("person",pf);
    return "success";
}

Bien évidemment, aucun traitement n'est effectué. Dans la réalité, c'est ici que le controller appelle le service qui doit réaliser la persistance de l'objet PersonForm dans la base par exemple.

Mais avant de le persister, une conversion vers un objet annoté correctement doit être faite. Ce sujet sera abordé dans un prochain article sur ce blog.

ETAPE 7: Test de l'application

Relancer la commande :

mvn tomcat:run

Puis saisir l'url localhost:8080/NOM_CONTEXT/person.html, compléter la saisie du formulaire puis cliquer sur le bouton OK.

En cas de succès, l'écran approprié s'affiche. Par exemple, si je saisis le nom: Agent 007 et sa date de naissance:12/12/1960, l'écran affiche:

blog_mvc3_success.PNG

Enfin, la page /WEB-INF/jsp/success.jsp contient ces quelques lignes:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Success Page</title>
</head><body><br/>
Utilisateur enregistré avec succes 
<br/><br/>Nom:
<c:out value="${person.name}"/>
<br/>Né(e) le: 
<c:out value="${person.dateBirth}"/>
</body>
</html>

Ici on a utilisé la taglib jstl pour afficher la personne créée.

Résumé de la seconde partie:

Nous avons introduit une stratégie de définition de la vue pour ne pas coupler le code java avec les aspects de présentation.

Ceci nous a conduit à adapter notre "controller" de la première partie.

Ce controller a été complété pour traiter l'URI GET /person.html et l'URI POST /create.html.

La première URI permet d'afficher le formulaire de saisie vide.

La seconde URI permet d'enregistrer les données saisies dans la base (par exemple).

Nous n'avons pas abordé la persistance des données mais nous y reviendrons bientôt dans ce blog.

J'espère que ce billet vous a facilité la prise en main de SpringMVC.

Nous introduisons dans la suite de cet article, Spring-JS (Javascript de Spring) & ses DOJO afin de mieux présenter la vue.

Aussi, nous nous intéressons à la validation de la saisie.

Tous ces aspects vont illustrer les simplifications apportées par SpringMVC.

Un commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.