JavaWeb(1):Servlet入门

参考书籍:Pro Java for Web Apps
注:以下内容需要HTTP基础,演示所用Java版本为Java8,所用容器为Tomcat8.5.51

        Servlet是一个运行在Web服务器中的Java小程序,用于接收和响应来自Web客户端的请求,使用HTTP进行通信,是所有Web应用程序的核心类,也是唯一的既可以直接处理和响应用户请求,也可以将处理工作委托给应用中的其他部分的类。
        Servlet只是一个简单的接口,包含了相关的方法。大多数情况下Servlet都继承自 $javax.servlet.GenericServlet$ 类,该类只包含一个抽象的 $service$ 方法以及一些辅助方法。$service$ 方法会处理所有请求,然后返回对应的响应。我们可以使用$javax.servlet.http.HttpServlet$ 类用于响应HTTP请求,它继承自 $GenericServlet$ 并实现其 $service$ 方法,而对于响应HTTP请求的方法只是空实现。在 $HttpServlet$ 类中,HTTP请求包括 $GET,HEAD,POST,PUT,DELETE,OPTIONS,TRACE$ 对应的响应方法名称为 $do +$ 首字母大写的请求名称。如 $GET$ 对应 $doGet()$ ,$POST$ 对应 $doPost()$ 等。除了响应方法之外,Servlet类中还包含 $init$ 方法和 $destroy$ 方法,分别在启用和关闭Servlet时调用,不过通常这些方法什么也不做。

         大多数情况下我们的Servlet类都是继承 $HttpServlet$ 类。对于上述提到的所有方法,它们都接收两个参数,一个是 $javax.servlet.http.HttpServletRequest$ 类型的参数,另一个是 $javax.servlet.http.HttpServletResponse$ 类型。顾名思义,$HttpServletRequest$ 指向客户端请求,对其我们有如下常用方法:

方法 作用
$getParameter$ 返回参数的单个值
$getParameterValues$ 返回参数的值的数组
$getParameterMap$ 返回一个包含所有参数名值对的$java.util.Map<String, String[\ \ ]>$
$getParameterNames$ 返回所有可用参数的名字的枚举
$getContentLength$ 返回请求正文的长度(小于 $2$ GB)
$getContentLengthLong$ 返回请求正文的长度(大于 $2$ GB)
$getCharacterEncoding$ 返回请求内容的字符编码
$getReader$ 返回一个 $java.io.BufferedReader$ 类,可以用于读取请求的内容
$getRequestURL$ 返回客户端用于创建请求的完整URL
$getRequestURI$ 返回URL中的服务器路径部分
$getServletPath$ 返回匹配Servlet映射的URL部分
$getHeader$ 返回指定名称的第一个值
$getHeaders$ 返回指定名称的所有值的枚举
$getHeaderNames$ 返回请求中所有头数据的名称的枚举
$getIntHeader$ 返回所有值为整型的头数据的一个值
$getDateHeader$ 返回可以识别为有效时间戳的头数据的时间戳

而$HttpServletResponse$ 指向服务端响应,我们有如下常用方法:

方法 作用
$getOutputStream$ 返回一个 $javax.servlet.ServletOutputStream$ 类,向响应中输出二进制数据
$getWriter$ 返回一个 $java.io.PrintWriter$ 类,向响应中输出字符
$setContentType$ 设置响应正文内容的类型
$setCharacterEncoding$ 设置响应内容的编码格式
$setHeader$ 设置指定名称头数据的值
$setIntHeader$ 设置指定值为整型的头数据的值
$setDateHeader$ 设置头数据时间戳
$setStatus$ 设置HTTP响应状态码
$getStatus$ 判断当前响应的状态
$sendError$ 设置状态码,一条可选的错误消息会输出到响应数据中,重定向到Web容器为客户端提供的错误页面并清空缓存
$sendRedirect$ 重定向客户端至另一个URL

        通过声明一个 Servlet类,我们的已经可以接受任何HTTP请求了,但由于我们并未重写空实现,因此我们无法响应请求,因此我们需要重写方法。

package com.wrox;

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 java.io.IOException;
import java.io.PrintWriter;

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("Hello world!");
    }
}

        $HttpServletResponse$ 中的 $getWriter(\ \ )$ 方法返回一个 $java.io.PrintWriter$ 类,可以将文本输入到输出流中。通过实现了 $doGet(\ \ )$ 方法,我们实现了对 $GET$ 请求的响应。
        虽然我们实现了响应请求,但是我们的程序并未部署。为了部署程序,我们要通过 $web.xml$ ,这个文件定义了程序中的监听器、Servlet、过滤器以及应用程序的设置。在 $WEB-INF$ 目录中创建 $web.xml$ 文件(如果没有的话)。通常我们看到的初始的 $web.xml$ 文件是这样的:

web.xml

        其中标签 $<web-app>$ 部分中的 $version$ 表明了程序所使用的 $Servlet API$ 版本。之后添加的内容均需要在 $<web-app>$ 中添加。
        如果我们要创建一个 $Servlet$ 实例,那么我们可以在文件添加如下内容:

<servlet>
    <servlet-name>helloServlet</servlet-name>
    <servlet-class>com.wrox.HelloServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

        可以发现 $<servlet-name>$ 中指定了 $Servlet$ 的名称,$<servlet-class>$ 指定了对应的类,而对于 $<load-on-startup>$ ,我们需要先知道,默认情况下 $init$ 方法是只有当接收到请求时才会调用的,而如果我们设置了 $<load-on-startup>$ 那么 $Servlet$ 将会按顺序启动 $Servlet$ ,而不是接收到请求后才启动。
        在部署之后,我们还需要告诉服务器我们所部署的 $Servlet$ 响应哪些请求:

<servlet-mapping>
    <servlet-name>helloServlet</servlet-name>
    <url-pattern>/greeting</url-pattern>
</servlet-mapping>

        上面的配置告诉程序所有 $/greeting$ 的请求都由 $helloServlet$ 处理。
        在处理完上述步骤之后,我们就可以运行了。我们在 $/greeting$ 上看到 $Hello\ \ world!$ 时,就代表我们的程序运行成功了。
        除了上面的方法,我们还可以通过使用 $@WebServlet$ 注解,如下所示:

@WebServlet (
    name = "helloServlet",
    urlPatterns = {"/greeting"},
    loadOnStartup = 1
)

        使用注解的缺点就是每次修改参数之后都要重新编译程序。