Servlet est une interface de programmation d'application (API) Java exécutée sur la machine serveur qui peut intercepter les demandes faites par le client et peut générer / envoyer une réponse en conséquence.

Servlets

Un servlet est une interface de programmation d'application (API) Java exécutée sur la machine serveur qui peut intercepter les demandes faites par le client et peut générer / envoyer une réponse en conséquence. Un exemple bien connu est le HttpServletqui fournit méthodes pour accrocher les HTTPrequêtes en utilisant les populaires Méthodes HTTPtelles que GETet POST. Vous pouvez configurer des HttpServletpour écouter sur un certain modèle d'URL HTTP, qui est configurable dans web.xml, ou plus récemment avec Java EE 6, avec @WebServletannotation. De nombreux frameworks Web Java EE sont construits sur des servlets, tels que JSF, JAX-RS, Spring MVC, Struts, Wicket, etc. Voir aussi Quelle est la différence entre JSF, Servlet et JSP?

Cycle de la vie

Lorsqu'un servlet est demandé pour la première fois ou au démarrage de l'application Web, le conteneur de servlet en crée une instance et la conserve en mémoire pendant la durée de vie de l'application Web. La même instance sera réutilisée pour chaque demande entrante dont l'URL correspond au modèle d'URL du servlet. Vous pouvez accéder aux données demandées par HttpServletRequestet gérer la réponse de HttpServletResponse. Les deux objets sont disponibles en tant qu'arguments de méthode à l'intérieur de l'une des méthodes substituées de HttpServlet, comme doGet()pour prétraiter une demande et doPost()à publier - traiter une demande. Voir aussi Comment fonctionnent les servlets? Instanciation, sessions, variables partagées et multithreading.

Installation

Pour exécuter des servlets, vous avez besoin de:

  • JDK (JRE n'est suffisant que si le serveur a son propre compilateur).
  • Un conteneur de servlet.
  • Facultativement, un IDE compatible Java EE (éditeur de développement intégré).

Il existe plusieurs conteneurs de servlets.

Il existe également des serveurs d'applications Java EE qui, à leur tour, contiennent également un conteneur de servlet en plus d'autres API Java EE telles que JSF, JPA, EJB, etc. Voir aussi Qu'est-ce que Java EE exactement?

L'installation d'un conteneur de servlet consiste généralement à télécharger le fichier zip / gz et à l'extraire à l'emplacement de votre choix.

En règle générale, vous souhaitez également utiliser un IDE tel que Eclipse, IntelliJou Netbeansafin que vous n'ayez pas besoin de compiler et de créer manuellement les fichiers source avec javac encore et encore. Les IDE décents ont des plugins pour intégrer de manière transparente le conteneur de servlet et importer les API Java EE nécessaires dans le chemin de génération du projet. Voir aussi Comment importer le javax API .servlet dans mon projet Eclipse?

Hello World # 1 (post-traiter une demande)

Le post-traitement d'une demande, comme l'envoi et la validation d'un formulaire POST, est le cas d'utilisation le plus connu pour une servlet. L'attribut action d'un HTML <form> peut pointer vers une URL de servlet et la method="post" déclencherait la méthode doPost() du servlet où vous avez toute la liberté de contrôler la requête HTTP et réponse.

En supposant qu'il existe un JSP dans /WEB-INF/hello.jsp qui ressemble à ceci ...

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

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Servlet Hello World</title>
        <style>.error { color: red; } .success { color: green; }</style>
    </head>
    <body>
        <form action="hello" method="post">
            <h1>Hello</h1>
            <p>
                <label for="name">What's your name?</label>
                <input id="name" name="name" value="${fn:escapeXml(param.name)}">
                <span class="error">${messages.name}</span>
            </p>
            <p>
                <label for="age">What's your age?</label>
                <input id="age" name="age" value="${fn:escapeXml(param.age)}">
                <span class="error">${messages.age}</span>
            </p>
            <p>
                <input type="submit">
                <span class="success">${messages.success}</span>
            </p>
        </form>
    </body>
</html>

(le fn:escapeXml() sert à protéger votre page contre XSStout en affichant de nouveau les entrées contrôlées par l'utilisateur; si JSTL ne le fait pas ne fonctionne pas en général, alors votre conteneur de servlet ne le prend pas en charge (comme Tomcat); vous pouvez l'installer en déposant simplement jstl-1.2.jar dans /WEB-INF/lib, voir aussi jstl)

... voici à quoi devrait ressembler la classe com.example.controller.HelloServlet:

package com.example.controller;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

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

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // Preprocess request: we actually don't need to do any business stuff, so just display JSP.
        request.getRequestDispatcher("/WEB-INF/hello.jsp").forward(request, response);
    }
    
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // Postprocess request: gather and validate submitted data and display the result in the same JSP.

        // Prepare messages.
        Map<String, String> messages = new HashMap<String, String>();
        request.setAttribute("messages", messages);

        // Get and validate name.
        String name = request.getParameter("name");
        if (name == null || name.trim().isEmpty()) {
            messages.put("name", "Please enter name");
        } else if (!name.matches("\\p{Alnum}+")) {
            messages.put("name", "Please enter alphanumeric characters only");
        }

        // Get and validate age.
        String age = request.getParameter("age");
        if (age == null || age.trim().isEmpty()) {
            messages.put("age", "Please enter age");
        } else if (!age.matches("\\d+")) {
            messages.put("age", "Please enter digits only");
        }

        // No validation errors? Do the business job!
        if (messages.isEmpty()) {
            messages.put("success", String.format("Hello, your name is %s and your age is %s!", name, age));
        }

        request.getRequestDispatcher("/WEB-INF/hello.jsp").forward(request, response);
    }
    
}

Compilez le code et placez-le dans le dossier /WEB-INF/classes. Dans ce cas particulier, le fichier de classe doit se retrouver dans /WEB-INF/classes/com/example/controller/HelloServlet.class. Un IDE comme Eclipse, Netbeans ou IntelliJ fera tout automatiquement lorsque vous aurez créé un projet Web dynamique.

Notez que l'annotation @WebServletne fonctionne que sur Java EE 6 / Conteneurs compatibles avec Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6, etc.) et si un fichier /WEB-INF/web.xml est présent, alors sa déclaration racine <web-app> doit également être conforme à la version Servlet 3.0.

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
    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_3_0.xsd"
    version="3.0">

</web-app>

Si vous utilisez / ciblez une ancienne version de servlet, telle que Servlet 2.5, vous devez supprimer l'annotation et mapper le servlet dans le fichier /WEB-INF/web.xml comme suit, ce qui fait effectivement la même chose:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5"> 

    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.example.controller.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

Dans les deux cas, il indique essentiellement au conteneur de servlet qu'il doit effectuer les opérations suivantes sous les couvertures:

HelloServlet helloServlet = new HelloServlet(); // Construct servlet.
helloServlet.init(servletConfig); // Initialize servlet with config.
helloServlet.init(); // Initialize servlet without config.
servlets.put("/hello", helloServlet); // Add to servlet mapping.

Déployez l'application Web et accédez à http: // localhost: 8080 / contextname / hello (sans l'extension .jsp!) Pour ouvrir la page Hello World. Lorsque vous ouvrez la page de cette manière, en entrant l'URL dans la barre d'adresse ou en suivant un lien ou un signet, une requête HTTP GET sera lancée et la méthode doGet() du servlet sera appelée. Lorsqu'un formulaire avec method="post" est soumis sur l'URL du servlet, une requête HTTP POST sera lancée et la méthode doPost() du servlet sera appelée.

Notez que le JSP a été placé dans le dossier /WEB-INF pour empêcher l'accès direct au JSP lorsqu'un utilisateur entre son URL dans la barre d'adresse du navigateur. Ceci est obligatoire lorsqu'il est nécessaire d'appeler la méthode doGet() du servlet avant que le JSP ne soit affiché, par exemple lorsque certaines données doivent être préchargées.

Hello World # 2 (prétraiter une demande)

Le prétraitement d'une demande telle que le préchargement d'une liste qui doit être présentée immédiatement sur une demande GET "simple vanille" (qui est utilisé lorsque vous suivez un lien / signet ou entrez vous-même l'URL dans l'adresse du navigateur) est un cas d'utilisation moins connu pour une servlet. Bien qu'il soit également largement utilisé dans le monde réel, le didacticiel de base moyen sur les servlets trouvé sur Internet ne l'explique pas du tout. Cependant, c'est assez simple: il vous suffit d'implémenter le travail métier dans la méthode doGet() au lieu de dans doPost().

Voici un exemple de lancement de base où nous obtenons une liste de produits de la base de données afin qu'elle puisse être présentée immédiatement lorsque l'utilisateur final ouvre la page produit d'une boutique en ligne. Seule la classe ProductService dans l'exemple ci-dessous est une autre classe personnalisée et non décrite dans ce wiki car elle dépasse la portée, mais sa méthode list() devrait être assez simple. L'exemple ci-dessous suppose qu'il s'agit d'un EJB, mais cela peut être n'importe quoi, voir aussi par exemple ce post.

package com.example.controller;

import java.io.IOException;
import java.util.List;

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

import com.example.business.ProductService;
import com.example.model.Product;

@WebServlet("/products")
public class ProductServlet extends HttpServlet {

    @EJB
    private ProductService productService;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // Preprocess request: load list of products for display in JSP.
        List<Product> products = productService.list();
        request.setAttribute("products", products);
        request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response);
    }
    
}

Voici à quoi devrait ressembler le /WEB-INF/products.jsp:

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

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Our Products</title>
    </head>
    <body>
        <h1>Products</h1>
        <table>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Description</th>
                <th>Price</th>
            </tr>
            <c:forEach items="${products}" var="product">
                <tr>
                    <td>${product.id}</td>
                    <td><c:out value="${product.name}" /></td>
                    <td><c:out value="${product.description}" /></td>
                    <td><fmt:formatNumber value="${product.price}" type="currency" /></td>
                </tr>
            </c:forEach>
       </table>
    </body>
</html>

(le <c:out> sert à protéger votre page contre les XSStout en réaffichant les entrées contrôlées par l'utilisateur, cela fonctionne efficacement la même chose que fn:escapeXml())

Déployez l'application Web et accédez à http: // localhost: 8080 / contextname / products (sans l'extension .jsp!). Il appellera la méthode doGet() du servlet qui charge les produits de la base de données, les stocke dans la portée de la requête et transmet la requête / réponse pour présenter les résultats.

Pour aller plus loin, vous pouvez filtrer les produits en fonction d'un paramètre de demande obtenu à partir d'un formulaire de recherche GET comme suit:

<form action="products">
    <input type="text" name="query" />
    <input type="submit" value="Search" />
</form>

Ou un lien hypertexte (ou un signet) comme suit:

<a href="products?query=java">Search for products with keyword "java"</a>

Avec

String query = request.getParameter("query");
List<Product> products = productService.find(query);
// ...

C'est aussi ainsi que fonctionnent les moteurs de recherche comme Google!

Style de codage et recommandations

  • N'appelez PAS une méthode doGet() à partir d'une méthode doPost() ou l'inverse, ou demandez-leur d'appeler toutes les deux une autre méthode courante comme processRequest(). C'est faux. Chacune de ces deux méthodes HTTP a sa propre responsabilité claire: prétraiter ou post-traiter une requête HTTP. Si vous avez l'intention de vous connecter à toutes les méthodes HTTP, vous devez remplacer la méthode service(). Voir également le modèle de contrôleur frontal.

  • NE PAS générer de code HTML dans un servlet à l'aide d'instructions out.print(). Cela ne fait que rendre la maintenance plus difficile. Le code HTML appartient à jspoù vous avez la liberté d'écrire du HTML comme vous le souhaitez sans manipuler les méthodes Java et les chaînes entre guillemets. De l'autre côté, n'utilisez PAS de scriptlets (code Java brut incorporé) dans les fichiers JSP. Cela ne fait que rendre la maintenance plus difficile. Le code Java appartient aux classes Java où vous avez la liberté d'écrire Java comme vous le souhaitez sans jouer avec les choses laides <% %>. Voir aussi Comment puis-je éviter le code Java dans les fichiers JSP, en utilisant JSP 2?

  • N'utilisez PAS <jsp:useBean> si vous utilisez déjà une servlet pour traiter le modèle. Cela entraînera seulement de la confusion et des problèmes de maintenance car le <jsp:useBean> suit un niveau d'approche MVC différent de celui utilisé lorsque vous utilisez des servlets. Ce sont soit des servlets, soit <jsp:useBean>, pas les deux.

Caractéristiques

Ressources en ligne et tutoriels

Questions fréquemment posées

Tags associés