Introduction à Spring MVC

Dans la guerre furieuse des frameworks MVC il y en a un qui se démarque par sa simplicité, sa facilité de prise en main et sa parfaite intégration à Spring : Spring MVC.

Certes Spring MVC n'est pas un framework événementiel comme pourraient l'être jsf, wicket, tapestry ou struts 2. Mais il possède de nombreux points d'extensions et utilisé avec Spring Web Flow il devient un framework très puissant.

Je vous propose ici une introduction des plus simples qui va consister à afficher une page Hello Spring MVC avec le framework Spring MVC.

Puis nous enrichirons progressivement notre application exemple avec de nouveaux tutoriels pour aboutir à l'utilisation de Spring Web Flow avec la gestion d'une transaction longue Hibernate.

Le projet est un projet Maven, tout ce qu'il y a de plus simple que vous pouvez télécharger ici.

Sinon je vous propose (et vous conseille) de le construire pas à pas avec moi :

Etape 1 : création du projet

Créons un projet web avec maven :

mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=com.netapsys.springmvc  -DartifactId=spring-mvc-webapp

Pour pouvoir utiliser les annotations notre projet doit être un projet en java 1.5. Ouvrez le pom.xml situé dans le répertoire que vous venez de créér spring-mvc-webapp et ajoutez ces informations entre les balises build. Vous devriez obtenir ceci :

<build>
  <finalName>spring-mvc-webapp</finalName>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.5</source>
        <target>1.5</target>
      </configuration>
    </plugin>	
    <plugin>
      <artifactId>maven-eclipse-plugin</artifactId>
      <version>2.4</version>
      <configuration>					
        <downloadSources>true</downloadSources>
        <downloadJavadocs>true</downloadJavadocs>					
      </configuration>
    </plugin>		
  </plugins>
</build>

Pour bien faire, j'indique aussi au plugin eclipse que je souhaite disposer des codes sources, ce qui est toujours un très bon choix quand on travail avec du code open-source.

Ensuite il faut en faire un projet eclipse : allez dans le répertoire que vous venez de créér et lancez la commande maven eclipse:eclipse

cd spring-mvc-webapp
mvn eclipse:eclipse

Puis importez le projet dans eclipse en vous étant assuré que la variable M2_REPO pointe bien vers votre répertoire ~/.m2/repository
Si ce n'est pas le cas : windows > preference > Java > Build Path > Class path variables puis New pour créer une nouvelle variable.

Par exemple pour moi M2_REPO = C:/Documents and Settings/michael_courcy/.m2/repository

Etape 2 : Exécution de l'application avec le serveur web jetty

Une fois notre projet créé nous allons modifier le pom.xml pour pouvoir exécuter ce projet sous le serveur web jetty :

Vous devriez obtenir ceci :

<build>
  <finalName>spring-mvc-webapp</finalName>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.5</source>
        <target>1.5</target>
      </configuration>
    </plugin>
    <plugin>
      <groupId>org.mortbay.jetty</groupId>
      <artifactId>maven-jetty-plugin</artifactId>
      <version>6.0.1</version>
      <configuration>
        <scanIntervalSeconds>10</scanIntervalSeconds>
      </configuration>
    </plugin>
  </plugins>
</build>

Remarquez qu'ici je n'ai fait que configurer jetty. Maven téléchargera lui-même jetty si nécessaire.

Vérifions que notre configuration fonctionne bien et exécutons l'application.

mvn jetty:run

Si vous n'avez pas l'habitude d'utiliser maven pour vos applications web, Maven téléchargera nécessairement de nombreuses dépendances transitives. C'est peut être l'occasion de prendre un café ...

Enfin, si tout s'est bien passé, vous pouvez vous connecter à l'url : http://localhost:8080/spring-mvc-webapp/

Pour y découvrir l'éternel Hello World.

Etape 3 : Branchement de Spring MVC

Spring mvc fonctionne par certains aspects de façon similaire à Struts : une servlet frontale va prendre l'ensemble des requêtes et les dispatcher à ses contrôleurs, ces contrôleurs sont des singletons issus d'une fabrique Spring.

Donc ce que je vais trouver dans web.xml :

  1. Une servlet pour dispatcher les requêtes aux contrôleurs.
  2. Un mapping de servlet

On aboutit à ce fichier web.xml :

<!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>
  <display-name>Archetype Created Web Application</display-name>
 
  <!-- déclare la servlet frontale (très similaire à struts)  -->
  <servlet>
    <servlet-name>spring-mvc-webapp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
  </servlet>
 
  <!-- Toutes les requêtes se terminant par .html seront servis par la servlet frontal -->
  <servlet-mapping>
    <servlet-name>spring-mvc-webapp</servlet-name>
    <url-pattern>*.html</url-pattern>
  </servlet-mapping>
 
</web-app>

Avant de recopier ce fichier, coupez jetty (Ctrl + C dans le terminal).

Ajoutez la dépendances à Spring et à spring-webmvc dans le pom.xml :

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring</artifactId>
  <version>2.5.3</version>
</dependency>
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>2.5.3</version>
</dependency>

puis régénérez votre projet eclipse :

mvn eclipse:clean eclipse:eclipse

enfin rafraîchissez votre projet eclipse quand l'opération est finie pour voir apparaitre vos librairies et éventuellement consulter le code source.

On remarque immédiatement à la lecture de web.xml qu'on ne voit pas comment on informe la servlet frontale des contrôleurs auxquels elle va devoir redispatcher les requêtes.

Par convention, si la servlet s'appelle spring-mvc-webapp, alors elle ira chercher sa fabrique de contrôleurs dans WEB-INF/spring-mvc-webapp-servlet.xml. C'est une convention qui peut être surdéfinie mais que nous allons suivre ici.

Voici donc le fichier spring-mvc-webapp-servlet.xml. Les commentaires détaillent chaque élément : je vous invite à les lire.

<?xml version="1.0" encoding="UTF-8"?>
<!--
  - Contexte d'application pour spring-mvc-webapp.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
		xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
 
  <!--
    - Tous les contrôleurs sont automatiquement détectés grâce à l'annotation @Controller.
    - On définit ici dans quel package le post processor doit chercher ces beans éventuellement annotés.
  -->
  <context:component-scan base-package="com.netapsys.springmvc.web"/>
 
    <!--
      - Les controlleurs de cette application fournissent une annotation @RequestMapping 
      - Qui peuvent être déclaré de deux manière différentes : 
      - Au niveau de la classe par exemple : 
      -     @RequestMapping("/addVisit.html")
      -     Pour ce type de controlleurs on peut annoter les méthodes pour qu'elles
      -     prennent en charge une requête Post ou une requête Get
      -     @RequestMapping(method = RequestMethod.GET)
      - Ou alors au niveau de chaque méthode, différents exemples  seront fournis.		 
    -->
 
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
      <!--
        Ceci est le view resolver, il permet de définir la technologie de vue utilisée et comment
        sélectionner une vue. Ici on prendra la solution la plus simple, elle permet de mapper 
        le nom de la vue retournée avec la sélection d'une jsp.
        Exemple si le nom de la vue retournée est "foo/bar/hello" alors on utilisera le fichier
        WEB-INF/jsp/foo/bar/hello.jsp pour constuire la vue. 
      -->
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/>
 
</beans>

Tout est en place maintenant pour écrire notre premier contrôleur et notre première jsp.

Etape 4 : Notre premier contrôleur

Créez le source folder /src/main/java/
et créez la classe HelloSpringMVC dans le package com.netapsys.springmvc.web

package com.netapsys.springmvc.web;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
 
/**
 * Contrôleur que l'on invoquera pour l'url /helloSpringMVC.html.
 * 
 * @author Michael Courcy
 *
 */
@Controller
@RequestMapping("/helloSpringMVC.html")
public class HelloSpringMVC {
 
  /**
    * Handler de la méthode Get pour l'URL /helloSpringMVC.html. 
    * 
    * @param name le nom que l'on doit afficher dans la vue.
    * @param model une map de toutes les données qui seront utilisables dans la vue 
    * @return le nom de la vue qu'il faudra utiliser.
    */
  @RequestMapping(method = RequestMethod.GET)
  public  String sayHelloWithSpringMVC(
    @RequestParam(value="name",required=false) String name, 
    ModelMap model) 
  {
    model.addAttribute("name",name);
    // on utilisera donc le fichier /WEB-INF/jsp/hello.jsp
    //au regard de la stratégie de résolution des vues 
    //utilisée dans cette application.
    return "hello";
  }
}

Plusieurs remarques sont à faire à la lecture de ce contrôleur :

  1. Le contrôleur n'hérite d'aucune classe particulière, il est identifié comme contrôleur grâce à l'annotation @Controller
  2. C'est encore une annotation qui permet de savoir quelle méthode invoquer pour une requê

te de type Get : @RequestMapping(method = RequestMethod.GET)

  1. Notre méthode prend un paramètre supplémentaire ModelMap qui est en fait un HashMap amélioré dans lequel on place toutes les informations que l'on souhaite disponibles lors de la génération de la vue avec la jsp.
  2. Le code n'utilise pas directement l'objet HttpServletRequest mais il indique encore grâce aux annotations comment renseigner les paramètres à partir du contenu de l'objet HttpServletRequest @RequestParam(value="name",required=false)

Ce dernier point est très important : ne pas utiliser directement l'objet HttpServletRequest nous permet de créer des tests unitaires très simplement sans avoir à créer de request mock.

Créez le source folder /src/test/java et créez aussi le package com.netapsys.springmvc.web pour y placer le test suivant

package com.netapsys.springmvc.web;
 
import org.springframework.ui.ModelMap;
 
import junit.framework.TestCase;
 
public class HelloSpringMCVTest extends TestCase {
 
  public void testSayHelloWithSpringMVC(){
    HelloSpringMVC helloSpringMVC = new HelloSpringMVC();
    ModelMap model = new ModelMap();
    String view = helloSpringMVC.sayHelloWithSpringMVC("mic", model);
    //vérifions que c'est bien la vue hello qui est générée
    assertEquals("hello", view);
    //vérifions que le modele contient bien un attribut name
    assertTrue(model.containsAttribute("name"));
    //vérifions que cet attribut name est bien "mic"
    assertEquals("mic",model.get("name"));
  }
}

Vous pouvez lancer ce test avec maven :

mvn -Dtest=HelloSpringMCVTest test

pour un test particulier ou alors

mvn test

pour refaire tous les tests.

Etape 5 : Notre propre jsp

Créez le fichier WEB-INF/jsp/hello.jsp :

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ page isELIgnored ="false" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Spring mvc hello !!</title>
</head>
<body>
 
Hello spring mvc vous donne votre nom qui est : ${name}
 
</body>
</html>

Il ne vous reste plus qu'à tester

mvn jetty:run

Puis à essayer l'url suivante : http://localhost:8080/spring-mvc-webapp/helloSpringMVC.html?name=mic

19 commentaires

  1. Super Michael !

    C’est exactement ce dont j’avais besoin !
    Merci beaucoup pour cet excellent article et pour les éléments envoyés.

  2. Hello Sani

    Lorsque http://localhost:8080/mywebapp/home… est invoqué, deux composants entrent en jeu : le controlleur et la vue.

    Dans le contexte qui est le notre ici il faut que ton controller soit annoté pour répondre à cette url :

    @RequestMapping(« /home.html »)

    Puis au regard de la stratégie de résolution de vue il faut que la méthode invoquée retourne la chaine de caractere « home », alors la vue selectionnée sera /WEB-INF/jsp/home.jsp

  3. Merci pour ce superbe tutoriel. Quelle simplicité, vraiment, de démarrer un projet avec maven…

    Un tout petit détail, le 3ème commentaire du servlet.xml n’est pas correctement fermé : « -> » au lieu de « –> ».

    Je serais très intéressé par une suite, avec par exemple l’ajout de Spring Web Flow, justement!

    Merci encore!

  4. Bravo je vous invite à continuer en integrant la sécurité avec ACEGI, Hibernat pour la perssistance, invocation de service web 😉

    Félicitation.

  5. Bonjour,
    J’ai vraiment apprécié ce tuto: c’est simple, clair et facile à suivre!
    Mais j’ai rencontré un problème à l’étape 3: après l’exécution de la commande « mvn eclipse:clean eclipse:eclipse », je n’ai pas trouvé de fichier nommé « spring-mvc-webapp.xml »:(
    Que quelqu’un m’aide…et merci d’avance!

  6. Merci beaucoup pour ce tuto, il m’a permis de bien commencer mon apprentissage de Spring MVC avec ses merveilleuses annotations.
    Aurais-tu une idée comment rajouter des contrôles sur les valeurs reçues ?
    Merci.

  7. Et un café et un mars ? 😉 C’est ma tournée !
    Toute blague mise à part, je prends note de l’intérêt pour ce sujet.

    PS : Vos contributions sont également les bienvenues !

  8. Super tuto !

    Simple et efficace.
    Le petit + qui fait la différence : la mise en place d’un serveur (Jetty) en 5 lignes XML et 1 ligne de commande.

    Bravo

  9. Merci pour ce tutoriel très utile,
    je voudrais juste faire une petite remarque
    je vois pas la source de la page helloSpringMVC.html ?
    Bien cordialement.

  10. C’est une vraie mine d’or. Il y aura-t-il une manière d’installer le pack à partir d’un fichier archive? sans connexion internet?

  11. Hi
    Thanks for your tuto

    Just had to use archetype:generate instead of archetype:create for maven 3.5.0 and the related plugins.

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.