Contents

Java Servlet简介

1. 概述

在本文中,我们将了解 Java 中 Web 开发的一个核心方面——Servlet。

2. Servlet 和容器

简而言之,Servlet 是一个处理请求、处理请求并返回响应的类。

例如,我们可以使用 Servlet 通过 HTML 表单收集用户的输入,从数据库中查询记录,以及动态创建网页。

Servlet 受另一个称为**Servlet 容器的 Java 应用程序的控制。**当在 Web 服务器中运行的应用程序接收到请求时,服务器将请求交给 Servlet 容器——后者又将其传递给目标 Servlet。

3. Maven依赖

要在我们的 Web 应用程序中添加 Servlet 支持,需要javax.servlet-api依赖项:

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

最新的 maven 依赖可以在这里 找到。

当然,我们还必须配置一个 Servlet 容器来部署我们的应用程序;这里 了解如何在 Tomcat 上部署 WAR。

4. Servlet 生命周期

让我们看一下定义 Servlet 生命周期的方法集。

4.1. init()

init方法被设计为只被调用一次。如果 servlet 的实例不存在,则 Web 容器:

  1. 加载 servlet 类
  2. 创建 servlet 类的实例
  3. 通过调用init方法对其进行初始化

init方法必须成功完成,servlet 才能接收任何请求。如果init方法抛出ServletException或未在 Web 服务器定义的时间段内返回,则 servlet 容器无法将 servlet 投入使用。

public void init() throws ServletException {
    // Initialization code like set up database etc....
}

4.2. service()

此方法仅在 servlet 的*init()*方法成功完成后调用。

Container 调用service()方法来处理来自客户端的请求,解释 HTTP 请求类型(GETPOSTPUTDELETE等)并根据需要调用doGetdoPostdoPutdoDelete等方法。

public void service(ServletRequest request, ServletResponse response) 
  throws ServletException, IOException {
    // ...
}

4.3. destroy()

由 Servlet 容器调用以使 Servlet 停止服务。

只有在 servlet 的服务方法中的所有线程都退出或经过超时时间之后,才会调用此方法。容器调用该方法后,不会在Servlet上再次调用该服务方法。

public void destroy() {
    // 
}

5. Servlet示例

请参考Tomcat根目录下部署应用 将javax-servlets-1.0-SNAPSHOT上下文根更改为 /:

<Context path=”/” docBase=”javax-servlets-1.0-SNAPSHOT”></Context>

在 Host 下 $CATALINA_HOME\conf\server.xml 中的标记

现在让我们设置一个使用表单处理信息的完整示例

首先,让我们定义一个带有映射*/calculateServlet*的 servlet ,它将捕获表单发布的信息并使用RequestDispatcher 返回结果:

@WebServlet(name = "FormServlet", urlPatterns = "/calculateServlet")
public class FormServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, 
      HttpServletResponse response)
      throws ServletException, IOException {
        String height = request.getParameter("height");
        String weight = request.getParameter("weight");
        try {
            double bmi = calculateBMI(
              Double.parseDouble(weight), 
              Double.parseDouble(height));
            
            request.setAttribute("bmi", bmi);
            response.setHeader("Test", "Success");
            response.setHeader("BMI", String.valueOf(bmi));
            request.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(request, response);
        } catch (Exception e) {
           request.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(request, response);
        }
    }
    private Double calculateBMI(Double weight, Double height) {
        return weight / (height * height);
    }
}

如上所示,使用@WebServlet 注解的类必须扩展javax.servlet.http.HttpServlet 类。需要注意的是,*@WebServlet *注解仅从 Java EE 6 开始可用。

@WebServlet 注解在部署时由容器处理,相应的 servlet 在指定的 URL 模式下可用。值得注意的是,通过使用注解定义 URL 模式,我们可以避免使用名为web.xml的 XML 部署描述符来进行 Servlet 映射。

如果我们希望映射没有注解的 Servlet,我们可以使用传统的web.xml代替:

<web-app ...>
    <servlet>
       <servlet-name>FormServlet</servlet-name>
       <servlet-class>com.root.FormServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FormServlet</servlet-name>
        <url-pattern>/calculateServlet</url-pattern>
    </servlet-mapping>
</web-app>

接下来,让我们创建一个基本的 HTML form

<form name="bmiForm" action="calculateServlet" method="POST">
    <table>
        <tr>
            <td>Your Weight (kg) :</td>
            <td><input type="text" name="weight"/></td>
        </tr>
        <tr>
            <td>Your Height (m) :</td>
            <td><input type="text" name="height"/></td>
        </tr>
        <th><input type="submit" value="Submit" name="find"/></th>
        <th><input type="reset" value="Reset" name="reset" /></th>
    </table>
    <h2>${bmi}</h2>
</form>

最后——为了确保一切都按预期工作,让我们也写一个快速测试:

public class FormServletLiveTest {
    @Test
    public void whenPostRequestUsingHttpClient_thenCorrect() 
      throws Exception {
        HttpClient client = new DefaultHttpClient();
        HttpPost method = new HttpPost(
          "http://localhost:8080/calculateServlet");
        List<BasicNameValuePair> nvps = new ArrayList<>();
        nvps.add(new BasicNameValuePair("height", String.valueOf(2)));
        nvps.add(new BasicNameValuePair("weight", String.valueOf(80)));
        method.setEntity(new UrlEncodedFormEntity(nvps));
        HttpResponse httpResponse = client.execute(method);
        assertEquals("Success", httpResponse
          .getHeaders("Test")[0].getValue());
        assertEquals("20.0", httpResponse
          .getHeaders("BMI")[0].getValue());
    }
}

6. Servlet、HttpServlet和JSP

重要的是要了解Servlet 技术不仅限于 HTTP 协议。

在实践中几乎总是如此,但*Servlet 是一个通用接口,而HttpServlet 是该接口的扩展——添加了 HTTP 特定的支持——例如doGetdoPost*等。

最后,Servlet 技术也是许多其他 Web 技术的主要驱动力,例如JSP – JavaServer Pages 、 Spring MVC 等。