Aller au menu - Aller au contenu

Icône Cycle de vie d'une servlet

Mise à jour : 24/07/2009
Difficulté : Difficile Difficile Creative Commons BY-NC-SA
10 160 visites depuis 7 jours, dont 293 sur ce chapitre classé 25/786
Vous avez appris comment les applications JEE sont construites : servlet + JSP + objets Java !

Dans ce chapitre vous apprendrez comment une servlet vient à la vie !
Comment Tomcat crée, utilise, initialise et invoque nos servlets.

Vous allez voir que ce dernier est vraiment très fort et qu'il nous soulage de beaucoup de travail.
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Initialisation de la servlet

Alors ! Accrochez-vous bien car il y aura beaucoup de choses à apprendre et à assimiler !
Déjà, vous aurez sans doute remarqué que nos servlets ne possèdent pas de constructeur.
Donc, comment sont-elles construites ?

Et je vous réponds : oui, nos servlets ont toutes un constructeur ! Mais il est invoqué automatiquement par le conteneur !
En gros, le conteneur, au moment de son lancement, va initialiser toutes les servlets en invoquant leurs constructeurs. Mais avant toute chose, le conteneur va aller lire le fichier web.xml. Les choses se passent un peu comme ça :

Image utilisateur


Voilà ; dans un premier temps, le conteneur construit un objet ServletContext contenant tous les couples "clé - valeur" spécifiés dans le fichier web.xml correspondant aux paramètres d'initialisation de contexte.

Ensuite, le conteneur continue son investigation et va construire puis initialiser les servlets présentes. Lors de l'instanciation d'une servlet par le conteneur, il se passe beaucoup de choses que vous ne soupçonnez même pas...
Voyons un peu.
En fait, lors de l'appel au constructeur de l'objet, le conteneur va aussi créer un objet ServletConfig contenant les paramètres d'initialisation de la servlet (couple clé - valeur). Cet objet contiendra aussi l'objet ServletContext.

Vous savez déjà que ceci :

Code : Java
1
ServletContext context = getServletContext();

est équivalent à ceci :

Code : Java
1
ServletContext context = getServletConfig().getServletContext();


Mais ce n'est réellement utile que si votre servlet n'hérite pas de HttpServlet ou de GenericServlet...
En effet, la méthode que vous avez utilisée plus haut provient de la classe GenericServlet dont hérite la classe HttpServlet ! ;)
Voici un petit diagramme de classe comme vous les aimez :

Image utilisateur


Vous pouvez voir que ladite méthode n'est présente qu'à partir du deuxième niveau de cette hiérarchie... Mais ne vous en faites pas trop, vous pouvez utiliser la première méthode que je vous ai montrée sans risque.
Oui, on voit bien, mais qu'est-ce que c'est que toutes ces méthodes init(ServletConfig config) ou service(HttpServletRequest request, HttpServletResponse response) ...

Ne vous en faites pas, nous y arrivons...

Vous voyez une méthode init(ServletConfig config) présente au plus haut niveau de la hiérarchie. Dans le constructeur, le conteneur crée l'objet ServletConfig et invoque ladite méthode.

Ceci pourrait être schématisé comme ceci :

Image utilisateur


La méthode init(ServletConfig config) qu'utilise le conteneur ne doit pas être redéfinie !

Si vous souhaitez faire quelques traitements avant d'utiliser votre servlet, comme l'initialisation d'objets, vous avez une autre méthode init() qui, elle, ne prend pas de paramètre. Cette méthode vous est offerte afin que vous puissiez y mettre ce que vous voulez.
Du coup, comment cette méthode est-elle appelée ?

C'est simple, c'est la méthode init(ServletConfig config) invoquée par le constructeur qui appelle la méthode init() que vous pouvez redéfinir. :D

Voici comment vous pouvez utiliser ceci :

Code : Java
 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
package com.servlet.test;

import java.io.IOException;
import java.io.PrintWriter;

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

public class DoIt extends HttpServlet{

	String str = "";
	public void init(){
		str = "<p>Paramètre initialisé dans la méthode init !</p>";
	}
	
	public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws IOException{
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("<h1>Servlet d'exemple ! </h1>");
		out.println(str);		
	}
	
}


Bon, tout ceci fait, votre servlet est initialisée et prête à être utilisée par le conteneur.
Donc voici ce qu'il se passe lorsque le conteneur créer la servlet. Et ça à chaque fois que le conteneur reçoit une requête HTTP ?

NON ! MON DIEU NON !
Je crois qu'il faut que je vous fasse aussi un petit topo sur la façon dont la servlet est utilisée... ;)

Utilisation de la servlet

Vous pensiez que les servlets étaient des objets instanciés à chaque requête HTTP, eh bien non !
Je sais que vous pourrez entendre ce genre de chose "Blablabla... chaque instance de ma servlet... Blablabla". Ceci est faux !
Il n'existe qu'une et une seule instance d'une servlet dans le conteneur !

Ce qu'il se passe c'est que pour chaque requête HTTP, un thread est créé pour satisfaire la requête et proposer une réponse !
Que se passe-t-il exactement ?

C'est simple et compliqué à la fois...
Le conteneur reçoit une requête HTTP. Vous savez déjà que ce dernier crée deux objets pour traiter la demande du client :
  • un objet HttpServletRequest ;
  • un objet HttpServletResponse.

Vous savez aussi que la servlet sait quelle méthode utiliser (doGet, doPost...) mais ne vous êtes-vous jamais demandé comment nos servlets savent faire ça ?
Si, bien sûr que si... Eh bien c'est grâce à la méthode service(HttpServletRequest request, HttpServletResponse response) que tout ceci est possible !
C'est dans cette dernière que le code permettant à la servlet de savoir quelle méthode utiliser se trouve ; par conséquent, il est très vivement déconseillé de redéfinir cette méthode !

Si je reprends la vie de notre servlet partant du principe que la servlet est initialisée, voici ce que ça donnerait :
  • la servlet reçoit une requête HTTP ;
  • elle crée les deux objets que vous connaissez ;
  • elle alloue un nouveau thread à cette requête ;
  • elle invoque la méthode service(HttpServletRequest request, HttpServletResponse response) en passant les deux objets en paramètres ;
  • ladite méthode invoque la bonne méthode de traitement (doGet, doPost...) ;
  • le traitement se fait ;
  • la réponse est renvoyée au client.


Je sais que vous êtes attachés à mes schémas... Donc je vous en ai préparé un pour reprendre mes dires :

Image utilisateur


Vous devez y voir plus clair, non ?

Euh, tu nous disais que Tomcat crée un thread pour chaque requête...

Oui, à chaque fois que celui-ci reçoit une requête HTTP, il crée les objets requête et réponse puis, il crée un thread afin d'invoquer la méthode service(HttpServletRequest req, HttpServletREsponse rep) . La suite, vous la connaissez... ;)

Je vous ai fait un petit schéma résumant ce qu'il se passe lorsque deux personnes envoient une requête vers la même ressource sur le serveur :

Image utilisateur


C'est assez explicite je pense...
Par contre, vous devez savoir encore une chose sur notre conteneur : il vous permet d'espionner vos applications... ^^

Le retour des listeners

Nous venons de voir comment sont construites et initialisées nos servlets mais il reste une notion, que j'approfondirai au fil du tuto et que vous devez connaître.
En fait, votre conteneur favori vous permet d'écouter toute la phase de construction de nos servlets et plus encore.

Au long du chapitre précédent, nous avons vu comment définir des objets dans notre contexte mais, parce qu'il y a un mais, ceux-ci étaient définis par une servlet... Pas terrible !...

Comment vous y prendriez-vous pour initialiser un objet au lancement de l'application ? (Une connexion SQL par exemple, mais un bête objet fera l'affaire.)

En définissant cet attribut à notre contexte dans la méthode init() de la servlet correspondant à notre page d'accueil.

Ah oui ? Et comment ferez-vous si je passe outre la page d'accueil, en saisissant une URL connue par exemple ?
Vous n'allez tout de même pas mettre cette instruction dans la méthode init() de toutes vos servlets ! :colere2:

Dans ces moments-là, la méthode public static void main(String[] args) vous manque réellement... :-°
Avoir un endroit où on peut faire ce que l'on veut avant le réel démarrage de l'application.

Cependant, comme je vous le disais dans le chapitre précédent, les concepteurs de la technologie sont de gentils concepteurs : ils vous ont donné le moyen d'initialiser des objets en dehors de toute servlet !

Et comment on fait ça ?

Avec de bons vieux événements, comme en programmation événementielle.
Durant toute la phase d'initialisation de notre application, le conteneur vous offre la possibilité d'écouter des événements afin de pouvoir faire des actions spécifiques à ce moment-là.

Bon : à partir de maintenant, tous les ZérOs qui ne savent pas comment les événements graphiques sont gérés en Java sont priés d'aller faire un tour sur le tuto concernant le pattern observer !

Alors, le but du jeu est de pouvoir initialiser un objet avant qu'une seule servlet ne soit instanciée, ceci afin que ce qui est fait à ce moment (l'initialisation de l'application) puisse être utile à toute l'application.
Déjà, avec ma phrase, vous avez dû comprendre que l'événement que nous allons utiliser était attaché au contexte de notre application : une seule chose est commune à l'application, le contexte. :)

Alors qui dit événement, dit interface à implémenter et, concernant le contexte, nous allons implémenter l'interface : ServletContextListener.

Image utilisateur


Vous pouvez voir que cette interface nous donne la possibilité d'agir lors de la création du contexte ou lors de sa destruction.
Alors, vu que vous êtes friands d'exemples en tout genre, nous allons faire un test.
Nous allons faire une servlet qui appelle un attribut attaché à l'objet ServletContext mais défini lors de la création dudit objet.
Pour faire ceci, nous allons avoir besoin :
  • d'une servlet ;
  • d'un objet servant à écouter l'événement ;
  • d'un objet codé par nos soins.


J'ai fait un nouveau projet Tomcat et voici ce que j'y ai mis.
Un objet métier que vous avez déjà vu tout à l'heure, l'objet ZColor.

Fichier ZColor.java



Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.sdz.test;

public class ZColor {

	private String color = "";
	public ZColor(String color){
		this.color = color;
	}
	
	public String toString(){
		return "COULEUR -> " + this.color;
	}
}


Bon, vous connaissez cet objet, rien à ajouter à son sujet... ^^
Maintenant, nous allons ajouter un objet ZColor à notre contexte lors de sa création, ceci via la classe qui suit.
Il s'agit de notre classe qui va se charger d'écouter aux portes de notre conteneur... Une fois le contexte créé, la méthode contextInitialized(ServletContextEvent event) sera invoquée.

Fichier ContextListener.java



Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.sdz.test;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class ContextListener implements ServletContextListener{

	public void contextDestroyed(ServletContextEvent event) {}

	public void contextInitialized(ServletContextEvent event) {
		//Nous avons accès à l'objet ServletContext via l'objet ServletContextEvent
		event.getServletContext().setAttribute("color", new ZColor("ROUGE"));
	}
}


Maintenant, construisons une servlet qui aura pour rôle d'utiliser l'objet initialisé dans le contexte.

Fichier Index.java



Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.sdz.test;

import java.io.IOException;
import java.io.PrintWriter;

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

public class Index extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
						throws IOException, ServletException{
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("<h2>Alors le contexte ?</h2>");
		out.println("<p> " + getServletContext().getAttribute("color") + " </p>");
	}	
}


Voilà, nous avons presque terminé, nous n'avons plus qu'à configurer le contexte de notre application avec le fichier web.xml.

Fichier web.xml



Code : XML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<web-app>
	
	<servlet>
		<servlet-class>com.sdz.test.Index</servlet-class>
		<servlet-name>IndexServlet</servlet-name>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>IndexServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<listener>
		<listener-class>com.sdz.test.ContextListener</listener-class>
	</listener>
	
</web-app>


Oh ! Une nouveauté !

Oui, difficile de ne pas la voir...
Vous l'aurez compris, ces balises servent à dire à Tomcat que des classes sont à utiliser comme des listeners.
Alors démarrez Tomcat et allez sur notre test, et voici ce que vous devriez avoir sous les yeux :

Image utilisateur


Pratique, n'est-ce pas ?
Vous venez de créer des attributs pour toute votre application sans ajouter une ligne de code à vos servlets.

En effet, en plus ce n'est pas trop dur... :p

Ah, je peux compliquer la tâche alors ! :diable:


Des listeners, encore des listeners

Puisque vous êtes chauds, c'est le bon moment pour vous dire que vous avez plusieurs listeners à votre disposition.
Vous venez de voir comment utiliser celui qui est invoqué lorsque le contexte est initialisé, mais il y en a d'autres ! ;)

En voici une liste :
  • ServletContextListener ;
  • ServletContextAttributeListener ;
  • ServletRequestListener ;
  • SerlvetRequestAttributeListener ;
  • HttpSessionListener ;
  • HttpSessionAttributeListener ;
  • HttpSessionBindingListener ;
  • HttpSessionActivateListener.


Je ne vous parlerai que des quatre premiers pour le moment puisque les autres concernent les sessions : nous n'avons pas encore parlé des sessions.
Par contre, pour les premiers, voici à quoi ils correspondent :
  • ServletContextListener : invoqué lorsque l'objet ServletContext est créé ;
  • ServletContextAttributeListener : invoqué lorsqu'un attribut est ajouté à l'objet ServletContext ;
  • ServletRequestListener : invoqué lorsque l'objet ServletRequest est créé ;
  • SerlvetRequestAttributeListener : invoqué lorsqu'un attribut est ajouté à l'objet ServletRequest .


Voici le détail de ces interfaces avec leurs méthodes :

Image utilisateur


En rouge vous pouvez voir les événements relatifs au contexte et en vert les événements relatifs à la requête.
Nous allons réaliser un petit exemple. Nous allons donc utiliser ces événements, ceci avec des classes qui auront pour rôle de compter le nombre d'attributs affectés à nos différents objets.

Voici ci-dessous les objets qui auront cette fonction.

Fichier CountContext.java



Code : Java
1
2
3
4
5
6
7
8
9
package com.sdz.test;

public class CountContext {

	private static int count = 0;
	
	public static void incremente(){++count;}
	public static int getCount(){return count;}	
}


Fichier CountContextAttribute.java



Code : Java
1
2
3
4
5
6
7
8
package com.sdz.test;

public class CountContextAttribute {
	private static int count = 0;
	
	public static void incremente(){++count;}
	public static int getCount(){return count;}
}


Fichier CountRequest.java



Code : Java
1
2
3
4
5
6
7
8
package com.sdz.test;

public class CountRequest {
	private static int count = 0;
	
	public static void incremente(){++count;}
	public static int getCount(){return count;}
}


J'ai fait trois objets pour être sûr que vous ne m'accuseriez pas de tricherie. :ange:
Rien de compliqué ici, parce que, si vous ne comprenez pas la fonction de ces objets, retour au tuto Java illico presto !

Maintenant, voici les objets implémentant les interfaces mentionnées plus haut (je ne les ai pas toutes mises).

Fichier ContextListener.java



Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.sdz.test;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class ContextListener implements ServletContextListener{

	public void contextDestroyed(ServletContextEvent event) {}

	public void contextInitialized(ServletContextEvent event) {
		event.getServletContext().setAttribute("color", new ZColor("ROUGE"));
		CountContext.incremente();
	}
}


Fichier ContextAttributeListener.java



Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.sdz.test;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;

public class ContextAttributeListener implements ServletContextAttributeListener{

	public void attributeAdded(ServletContextAttributeEvent event) {
		CountContextAttribute.incremente();
	}
	public void attributeRemoved(ServletContextAttributeEvent event) {}
	public void attributeReplaced(ServletContextAttributeEvent event) {}
}



Fichier RequestListener.java



Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package com.sdz.test;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

public class RequestListener implements ServletRequestListener{

	public void requestDestroyed(ServletRequestEvent event) {
		CountRequest.incremente();
	}
	public void requestInitialized(ServletRequestEvent event) {}
}


Il ne manque plus que la servlet de test ainsi que le fichier web.xml.
J'ai modifié un peu la servlet afin d'isoler notre test. J'ai aussi ajouté les listeners dans le fichier web.xml.

Fichier Index.java



Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.sdz.test;

import java.io.IOException;
import java.io.PrintWriter;

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

public class Index extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
						throws IOException, ServletException{
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		
		out.println("<p>Nombre d'objet(s) ServletContext : " + CountContext.getCount() + "</p>");
		out.println("<p>Nombre d'attribut(s) du contexte : " + CountContextAttribute.getCount() + "</p>");
		out.println("<p>Nombre d'objet(s) HttpServletRequest : " + CountRequest.getCount() + "</p>");		
	}	
}


Fichier web.xml



Code : XML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<web-app>
	
	<servlet>
		<servlet-class>com.sdz.test.Index</servlet-class>
		<servlet-name>IndexServlet</servlet-name>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>IndexServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<listener>
		<listener-class>com.sdz.test.ContextListener</listener-class>
		<listener-class>com.sdz.test.ContextAttributeListener</listener-class>
		<listener-class>com.sdz.test.RequestListener</listener-class>
	</listener>
	
</web-app>


Ceci fait, il ne vous reste plus qu'à relancer Tomcat et de retourner sur notre test. Vous devriez obtenir ceci (j'ai appuyé quelquefois sur F5) :

Image utilisateur


Notre système de listeners fonctionne à merveille, vous avez appris à écouter les événements d'initialisation et à les utiliser pour paramétrer votre application : mes compliments !

Bon, je crois que vous en avez assez vu pour l'instant. Rendez-vous au QCM !

Q.C.M.

Tomcat crée une instance de vos servlets à chaque requête HTTP entrante.
Laquelle de ces deux méthodes pouvez-vous redéfinir ou surcharger ?
  • init(ServletConfig conf)
  • init()

L'objet ServletContext contient un objet ServletConfig.
Vrai ou faux ?
Quelle balise XML permet d'introduire des listeners dans le fichier web.xml ?

Statistiques de réponses au QCM

Voilà, maintenant vous êtes incollables sur la vie d'une servlet !
Vous pourrez même écrire un livre là-dessus. :p

Eh bien ne vous en déplaise, le conteneur vous cache encore pas mal de choses ! Il offre encore beaucoup de fonctionnalité et d'outils afin de vous faciliter la vie.

D'ailleurs, dans le prochain chapitre, je vous propose d'aborder une notion que vous allez beaucoup utiliser : les sessions.
Chapitre précédent Sommaire Chapitre suivant

Partager

12 commentaires pour "Cycle de vie d'une servlet"
Note moyenne : 3.17 / 4 (294 votes)
Pseudo Commentaire
Hors ligne lahcenvida # Posté le 06/08/2010 à 17:56:49

merci
Hors ligne Kohistan # Posté le 12/08/2010 à 02:18:50
Avatar

Études : Ecole des Mines de Nantes

Je m'étais déjà un peu initié avec les tutos d'AppEngine, mais ce tuto m'a appris pas mal de choses sur le conteneur, merci cysboy !

J'imagine qu'il y a encore plein de trucs puissants à faire, est-ce qu'une suite est prévue au programme ? J'aurais bien voulu aider mais là tout de suite j'ai pas le niveau :-° ..
Hors ligne choocky # Posté le 30/12/2010 à 10:38:18
Avatar

La suite ! :)
Hors ligne polmiki # Posté le 15/01/2011 à 23:21:47

Merci Cysboy!
Tes tutoriels sont excellents, j'ai lu l'ensemble du tutoriel sur Java, et puis je viens de finir le J2EE!
j'attends avec impatience la suite !
Je trouve le j2ee bien plus dur que Java!

j'aimerais aussi dire que parfois certains critiquent trop facilement, des gens qui ont 15 ans de Java dérriere eux, ils viennent sur des tutos pour débutant et recherche la p'tite bete pour critiquer gratuitement!
Et c'est normal que parfois tout n'est pas expliqué comme il faut, c'est pour les débutants et on part de zéros le reste sera eclaircit plus tard!!

Encore une fois j'aimerais remercier Cysboy pour le travail énorme qu'il fournit ça m'a beaucoup servit !
Hors ligne GNUt3ll4 # Posté le 24/03/2012 à 20:45:14
Avatar

Études : IUT A Lyon 1

Bonjour,

Tout d'abord merci pour tous tes tutoriels (on ne le dira jamais assez, le partage ça n'a pas de prix :) )
Je me permets de relever une petite faute de frappe :
Dans la sous-partie
Des listeners, encore des listeners
Plus précisément dans la première liste :
SerlvetRequestAttributeListener ; -> on a une inversion du l et du v de 'Servlet'
Et dans la liste des correspondances juste en dessous, la même, due à un copié/collé

Bonne continuation !

Voir tous les commentaires