Couche WEB
Etapes :
- le client fait une demande au contrôleur. Celui-ci voit passer toutes les demandes des clients. C'est la porte d'entrée de
l'application. C'est le C de MVC. Ici le contrôleur est assuré par une servlet générique (personnes) :
org.springframework.web.servlet.DispatcherServlet
- le contrôleur principal personnes [DispatcherServlet] fait exécuter l'action demandée par l'utilisateur par une classe implémentant
l'interface :
org.springframework.web.servlet.mvc.Controller
A cause du nom de l'interface, nous appellerons une telle classe un contrôleur secondaire pour le distinguer du contrôleur
principal personnes[DispatcherServlet] ou simplement contrôleur lorsqu'il n'y a pas d'ambiguïté. Le schéma ci-dessus présente les contrôleurs implémentés dans l'application. Il y a en général plusieurs contrôleurs, un par action.
- le contrôleur(un parmit les contrôleurs) [Controller] traite une demande particulière de l'utilisateur. Pour ce faire, il peut avoir besoin de l'aide de la
couche métier. Une fois la demande du client traitée, celle-ci peut appeler diverses réponses.
- le contrôleur choisit la réponse (= vue) à envoyer au client. Choisir la réponse à envoyer au client nécessite plusieurs
étapes :
- choisir l'objet qui va générer la réponse. C'est ce qu'on appelle la vue V, le V de MVC. Ce choix dépend en
général du résultat de l'exécution de l'action demandée par l'utilisateur.
- lui fournir les données dont il a besoin pour générer cette réponse. En effet, celle-ci contient le plus souvent des
informations calculées par la couche métier ou le contrôleur lui-même. Ces informations forment ce qu'on
appelle le modèle M de la vue, le M de MVC. Spring MVC fournit ce modèle 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 modèle M nécessaire à celle-ci.
- le contrôleur personnes [DispatcherServlet] demande à la vue choisie de s'afficher. Il s'agit d'une classe implémentant l'interface
org.springframework.web.servlet.View
Spring MVC propose différentes implémentations de cette interface pour générer des flux HTML, Excel, PDF, ... Le
schéma ci-dessus présente les vues implémentées dans l'application. Il y a en général plusieurs vues.
- le générateur de vue View utilise le modèle Map préparé par le contrôleur Controller pour initialiser les parties
dynamiques de la réponse qu'il doit envoyer au client.
- la réponse est envoyée au client. La forme exacte de celle-ci dépend du générateur 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 contrôleur traitant l'action demandée par l'utilisateur
Avec Spring MVC, c'est à partir de l'URL demandée par le client que le contrôleur principal personnes[DispatcherServlet] va choisir l'objet
[Controller] qui va traiter l'action. Le lien URL <-> Controller est fait par configuration.
Spring met à notre disposition diverses stratégies pour lier une URL à un objet [Controller]. Le choix d'une
stratégie particulière est fait dans le fichier de configuration [personnes-servlet.xml] de la servlet personnes de l'application en précisant
le nom d'une classe implémentant l'interface [org.springframework.web.servlet.HandlerMapping].Il existe plusieurs méthodes de résolution
possibles, c.a.d. plusieurs façons possibles de lier une URL à un objet [Controller]. La methode de résolution utilisée dans l'application est:
[SimpleUrlHandlerMapping] : avec cette classe, chaque URL [http://machine:port/chemin1/.../document ] devant
être traitée par l'application est déclarée dans le fichier de configuration de la servlet. Dans cette déclaration , on associe à
[/document] le nom de l'objet [Controller] qui doit la traiter.
Etapes 3-5 : traitement de la demande par l'objet [Controller]
L'objet [Controller] chargé de traiter l'action est une instance de classe implémentant l'interface
[org.springframework.web.servlet.mvc.Controller].
Dans notre application , le fichier [applicationContext.xml] joue le rôle de " parent " du fichier [personnes-servlet.xml] de définition des servlets de l'application.Spring permet en effet de créer une relation parent - fils entre deux fichiers de configuration. Les
beans définis dans le fichier parent sont automatiquement connus du fichier fils. C'est donc dans ce fichier que nous avons placé la configuration des
couches [métier] 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 doit charger une classe spéciale de
type [org.springframework.web.context.ContextLoaderListener]. Cette classe va exploiter le fichier [applicationContext.xml] et
instancier les beans qui y sont définis. Parce que ceux-ci doivent être instanciés avant ceux définis par le fichier [personnes-servlet.xml], la
classe [org.springframework.web.context.ContextLoaderListener] doit être instanciée avant la classe [DispatcherServlet] qui elle, va
exploiter le fichier [personnes-servlet.xml]. Cela est fait dans le fichier [web.xml].
C'est ainsi que le Le champ privé [service] des contrôleurs va donc être initialisé dans [personnes-servlet.xml] par le bean d'id "service" défini dans [applicationContext.xml]. Lorsque la méthode [handleRequestest] des contrôleurs est exécutée, le champ privé [service] aura donc déjà sa valeur. Nous pouvions nous même récupéré le bean d'id "service" défini dans [applicationContext.xml] par l'instrution "service = request.getSession().getServletContext().getAttribute("groupe");", mais nous avions préféré déléguer cette tâche a Spring.
Couches [DAO] et [SERVICE]
Dossier [src]
Ce dossier contient les codes source des couches [dao] et [service].
Dossier [dbannuiare]
Ce dossier est la base de donnée du projet. Il doit être copié dans le dossier [data] de MySql.
Dossier [lib] du dossier [WEB-INF]
Ce dossier contient les archives nécessaires à l’application
La couche [dao]
La couche [dao] est constituée des classes et interfaces suivantes :
- [IDao.java] est l’interface présentée par la couche [dao]
- [DaoImplCommon.java] Implémente l'interface IDao et dérive la classe
[SqlMapClientDaoSupport] de SpringORM (package org.springframework.orm.ibatis.support.SqlMapClientDaoSupport).
- [DaoException.java] est le type des exceptions non contrôlées, lancées par la couche [dao].
La couche d’accès aux données [iBATIS]
La classe SpringORM [SqlMapClientDaoSupport] utilise un framework tierce [Ibatis SqlMap] disponible à l’url
[http://ibatis.apache.org/]
L’utilisation du framework [iBATIS] nécessite deux archives [ibatis-common, ibatis-sqlmap] qui ont été toutes deux placées
dans le dossier [lib] du projet.
Parmi les méthodes de la classe [SqlMapClientDaoSupport], l’une d’elles permet de configurer le client [iBATIS] avec lequel nous avons exploité la base de
données: void setSqlMapClient(com.ibatis.sqlmap.client.SqlMapClient sqlMapClient)
L’objet [SqlMapClient sqlMapClient] est l’objet [IBATIS] utilisé pour accéder à la base de données. A lui tout seul, il
implémente la couche [iBATIS] de notre architecture.
Une séquence typique d’actions avec cet objet est la suivante :
- demander une connexion à un pool de connexions
- ouvrir une transaction
- exécuter une série d’ordres SQL mémorisée dans un fichier de configuration
- fermer la transaction
- rendre la connexion au pool
Si notre implémentation [DaoImplCommon] travaillait directement avec [iBATIS], elle devrait faire cette séquence de façon
répétée. Seule l’opération 3 est spécifique à une couche [dao], les autres opérations étant génériques. La classe Spring
[SqlMapClientDaoSupport] assurera elle-même les opérations 1, 2, 4 et 5, déléguant l’opération 3 à sa classe dérivée, ici la
classe [DaoImplCommon].
Pour pouvoir fonctionner, la classe [SqlMapClientDaoSupport] a besoin d’une référence sur l’objet iBATIS [SqlMapClient
sqlMapClient] qui va assurer le dialogue avec la base de données. Cet objet a besoin de deux choses pour fonctionner :
- un objet [DataSource] connecté à la base de données auprès duquel il va demander des connexions
- un (ou des) fichier de configuration où sont externalisés les ordres SQL à exécuter. En effet, ceux-ci ne sont pas dans
le code Java. Ils sont identifiés par un code dans un fichier de configuration et l’objet [SqlMapClient sqlMapClient]
utilise ce code pour faire exécuter un ordre SQL particulier.
Un embryon de configuration de notre couche [dao] qui refléterait l'architecture ci-dessus serait le suivant : (cette config est dans le fichier [applicationContext.xml])

Ici la propriété [sqlMapClient] (ligne 3) de la classe [DaoImplCommon] (ligne 2) est initialisée. Elle l’est par la méthode
[setSqlMapClient] de la classe [DaoImplCommon]. Cette classe n’a pas cette méthode. C’est sa classe parent
[SqlMapClientDaoSupport] qui l’a. C’est donc elle qui est en réalité initialisée ici.
Maintenant ligne 4, on fait référence à un objet nommé " sqlMapClient " qui reste à construire. Celui-ci, on l’a dit, est de type
[SqlMapClient], un type [iBATIS] : com.ibatis.sqlmap.client.SqlMapClient
[SqlMapClient] est une interface. SpringORM offre la classe [SqlMapClientFactoryBean] pour obtenir un objet implémentant cette
interface : org.springframework.orm.ibatis.SqlMapClientFactoryBean
Rappelons que nous cherchons à instancier un objet implémentant l’interface [SqlMapClient]. Ce n’est apparemment pas le cas
de la classe [SqlMapClientFactoryBean]. Celle-ci implémente l’interface [FactoryBean] (cf ci-dessus). Celle-ci a la méthode
[getObject()] suivante : Objet getObject();
Lorsqu’on demande à Spring une instance d’un objet implémentant l’interface [FactoryBean], il :
- crée une instance [I] de la classe - ici il crée une instance de type [SqlMapClientFactoryBean].
- rend à la méthode appelante, le résultat de la méthode [I].getObject() - la méthode
[SqlMapClientFactoryBean].getObject() va rendre ici un objet implémentant l’interface [SqlMapClient].
Pour pouvoir rendre un objet implémentant l’interface [SqlMapClient], la classe [SqlMapClientFactoryBean] a besoin de deux
informations nécessaires à cet objet :
- un objet [DataSource] connecté à la base de données auprès duquel il va demander des connexions
- un (ou des) fichier de configuration où sont externalisés les ordres SQL à exécuter
La classe [SqlMapClientFactoryBean] possède les méthodes set pour initialiser ces deux propriétés :
void setConfigLocation(Ressource configLocation); void setDatasource(Datasource datasource);
Nous progressons... Notre fichier de configuration se précise et devient : (cette config est dans le fichier [applicationContext.xml])

- lignes 2-3 : le bean " sqlMapClient " est de type [SqlMapClientFactoryBean]. De ce qui vient d’être expliqué, nous
savons que lorsque nous demandons à Spring une instance de ce bean, nous obtenons un objet implémentant
l’interface iBATIS [SqlMapClient]. C’est ce dernier objet qui sera donc obtenu en ligne 14.
- lignes 7-9 : nous indiquons que le fichier de configuration nécessaire à l’objet iBATIS [SqlMapClient] s’appelle " sql-
map-config-mysql.xml " et qu’il doit être cherché dans le ClassPath de l’application. La méthode
[SqlMapClientFactoryBean].setConfigLocation est ici utilisée.
- lignes 4-6 : nous initialisons la propriété [dataSource] de [SqlMapClientFactoryBean] avec sa méthode
[setDataSource].
Ligne 5, nous faisons référence à un bean appelé " dataSource " qui reste à construire.
Il existe diverses implémentations de l’interface [DataSource] disponibles librement. Nous allons utiliser ici l’implémentation
[commons DBCP] disponible à l’url [http://jakarta.apache.org/commons/dbcp/].
L’utilisation de l’outil [commons DBCP] nécessite deux archives [commons-dbcp, commons-pool] qui ont été toutes deux
placées dans le dossier [lib] du projet.
La classe [BasicDataSource] de [commons DBCP] fournit l’implémentation [DataSource] dont nous avons besoin :
org.apache.commons.dbcp.BasicDataSource
Cette classe va nous fournir un pool de connexions pour accéder à la base de donnée de notre application.
Pour cela, il faut lui donner les informations dont elle a besoin pour créer les connexions du pool :
- le nom du pilote JDBC à utiliser – initialisé avec [setDriverClassName]
- le nom de l’url de la base de données à exploiter - initialisé avec [setUrl]
- l’identifiant de l’utilisateur propriétaire de la connexion – initialisé avec [setUsername] (et non pas setUserName
comme on aurait pu s'y attendre)
- son mot de passe - initialisé avec [setPassword]
Le fichier de configuration de notre couche [dao] pourra être le suivant :(cette config est dans le fichier [applicationContext.xml])

- lignes 7-9 : le nom du pilote MySql
- lignes 11-13 : l’url de la base base de donnée. On fera particulièrement attention à l’écriture de celle-ci. Il
ne doit y avoir aucun espace entre les balises et l’url.
- lignes 14-16 : le propriétaire de la connexion – ici, [root] qui est l’administrateur
- lignes 17-19 : son mot de passe [root]
On a beaucoup progressé mais il reste toujours des points de configuration à élucider : la ligne 28 référence le fichier [sql-map-
config-mysql.xml] qui doit configurer le client [SqlMapClient] d’iBATIS.
Le fichier [sql-map-config-mysql.xml] est le suivant :

- ce fichier doit avoir comme balise racine (lignes 6 et 8)
- ligne 7 : la balise sert à désigner les fichiers qui contiennent les ordres SQL à exécuter.
Le fichier [personnes-mysql.xml] décrit les ordres SQL qui vont être émis sur la table [PERSONNES] de la base de données. Son contenu est le suivant :

- le fichier doit avoir comme balise racine (lignes 7 et 45)
- lignes 9-10 : pour faciliter l’écriture du fichier on donne l’alias (synonyme) [Personne.classe] à la classe
[istia.st.springmvc.personnes.entites.Personne].
- lignes 12-17 : fixe les correspondances entre colonnes de la table [PERSONNES] et champs de l’objet [Personne].
- lignes 19-43 : les ordres SQL [select]
La couche [service]
L’implémentation [ServiceImpl] détient une référence sur la couche [dao]. Celle-ci, sera initialisée par Spring au moment de l’instanciation de la couche [service - ServiceImpl]. Le fichier [applicationContext.xml] contient l’instanciation de la couche [service] :

lignes 37-42 : configurent la couche [service]
|