Servlets - 快速指南

Servlet - 概述

什么是 Servlet?

Java Servlet 是在 Web 或应用程序服务器上运行的程序,充当来自 Web 浏览器或其他 HTTP 客户端的请求与 HTTP 服务器上的数据库或应用程序之间的中间层。

使用 Servlet,您可以通过网页表单收集用户的输入,显示来自数据库或其他来源的记录,并动态创建网页。

Java Servlet 通常与使用通用网关接口 (CGI) 实现的程序具有相同的用途。但与 CGI 相比,Servlet 具有几个优势。

  • 性能明显更好。

  • Servlet 在 Web 服务器的地址空间内执行。没有必要创建单独的进程来处理每个客户端请求。

  • Servlet 是独立于平台的,因为它们是用 Java 编写的。

  • 服务器上的 Java 安全管理器强制执行一组限制来保护服务器计算机上的资源。因此 servlet 是值得信赖的。

  • Java 类库的全部功能可供 servlet 使用。它可以通过您已经看到的套接字和 RMI 机制与小程序、数据库或其他软件进行通信。

Servlet 架构

下图显示了 Servlet 在 Web 应用程序中的位置。

Servlets 架构

Servlet 任务

Servlet 执行以下主要任务 −

  • 读取客户端(浏览器)发送的显式数据。这包括网页上的 HTML 表单,也可能来自小程序或自定义 HTTP 客户端程序。

  • 读取客户端(浏览器)发送的隐式 HTTP 请求数据。这包括浏览器能够理解的 cookie、媒体类型和压缩方案等。

  • 处理数据并生成结果。此过程可能需要与数据库通信、执行 RMI 或 CORBA 调用、调用 Web 服务或直接计算响应。

  • 将显式数据(即文档)发送到客户端(浏览器)。此文档可以采用多种格式发送,包括文本(HTML 或 XML)、二进制(GIF 图像)、Excel 等。

  • 将隐式 HTTP 响应发送到客户端(浏览器)。这包括告诉浏览器或其他客户端返回的文档类型(例如 HTML)、设置 cookie 和缓存参数以及其他此类任务。

Servlet 包

Java Servlet 是由具有支持 Java Servlet 规范的解释器的 Web 服务器运行的 Java 类。

可以使用 javax.servletjavax.servlet.http 包创建 Servlet,这两个包是 Java 企业版的标准部分,是支持大型开发项目的 Java 类库的扩展版本。

这些类实现了 Java Servlet 和 JSP 规范。在撰写本教程时,版本是 Java Servlet 2.5 和 JSP 2.1。

Java servlet 的创建和编译方式与任何其他 Java 类一样。安装 servlet 包并将其添加到计算机的 Classpath 后,您可以使用 JDK 的 Java 编译器或任何其他当前编译器编译 servlet。

下一步是什么?

我将逐步指导您设置环境以开始使用 Servlet。因此,系好安全带,开始使用 Servlet 吧。我相信您会非常喜欢本教程。

Servlet - 环境设置

开发环境是您开发 Servlet、测试并最终运行它们的地方。

与任何其他 Java 程序一样,您需要使用 Java 编译器 javac 编译 servlet,编译 servlet 应用程序后,它将被部署在配置的环境中以进行测试和运行。

此开发环境设置涉及以下步骤 −

设置 Java 开发工具包

此步骤涉及下载 Java 软件开发工具包 (SDK) 的实现并适当设置 PATH 环境变量。

您可以从 Oracle 的 Java 站点下载 SDK − Java SE 下载

下载 Java 实现后,请按照给定的说明进行安装和配置设置。最后,设置 PATH 和 JAVA_HOME 环境变量以引用包含 java 和 javac 的目录,通常分别为 java_install_dir/bin 和 java_install_dir。

如果您运行的是 Windows 并将 SDK 安装在 C:\jdk1.8.0_65 中,则应将以下行放入 C:\autoexec.bat 文件中。

set PATH = C:\jdk1.8.0_65\bin;%PATH%
set JAVA_HOME = C:\jdk1.8.0_65

或者,在 Windows NT/2000/XP 上,您也可以右键单击"我的电脑",选择"属性",然后选择"高级",再选择"环境变量"。然后,您将更新 PATH 值并按下 OK 按钮。

在 Unix(Solaris、Linux 等)上,如果 SDK 安装在 /usr/local/jdk1.8.0_65 中,并且您使用 C shell,则应将以下内容放入 .cshrc 文件中。

setenv PATH /usr/local/jdk1.8.0_65/bin:$PATH
setenv JAVA_HOME /usr/local/jdk1.8.0_65

或者,如果您使用集成开发环境 (IDE),如 Borland JBuilder、Eclipse、IntelliJ IDEA 或 Sun ONE Studio,请编译并运行一个简单的程序以确认 IDE 知道您安装 Java 的位置。

设置 Web 服务器 − Tomcat

市场上有许多支持 servlet 的 Web 服务器。一些 Web 服务器可以免费下载,Tomcat 就是其中之一。

Apache Tomcat 是 Java Servlet 和 Java Server Pages 技术的开源软件实现,可以作为测试 servlet 的独立服务器,并可以与 Apache Web 服务器集成。以下是在计算机上设置 Tomcat 的步骤 −

  • https://tomcat.apache.org/ 下载最新版本的 Tomcat。

  • 下载安装程序后,将二进制分发包解压到方便的位置。例如在 Windows 上在 C:\apache-tomcat-8.0.28 中,或在 Linux/Unix 上在 /usr/local/apache-tomcat-8.0.289 中创建指向这些位置的 CATALINA_HOME 环境变量。

在 Windows 机器上执行以下命令即可启动 Tomcat −

%CATALINA_HOME%\bin\startup.bat
或
C:\apache-tomcat-8.0.28\bin\startup.bat

在 Unix(Solaris、Linux 等)机器上执行以下命令即可启动 Tomcat −

$CATALINA_HOME/bin/startup.sh
或
/usr/local/apache-tomcat-8.0.28/bin/startup.sh

启动后,访问 http://localhost:8080/ 即可使用 Tomcat 附带的默认 Web 应用程序。如果一切正常,则应显示以下结果 −

Tomcat 主页

有关配置和运行 Tomcat 的更多信息,请参阅此处包含的文档以及 Tomcat 网站 − http://tomcat.apache.org

在 Windows 机器上执行以下命令即可停止 Tomcat −

C:\apache-tomcat-8.0.28\bin\shutdown

在 Unix(Solaris、Linux 等)机器上执行以下命令即可停止 Tomcat −

/usr/local/apache-tomcat-8.0.28/bin/shutdown.sh

设置 CLASSPATH

由于 servlet 不是 Java 平台标准版的一部分,因此您必须向编译器标识 servlet 类。

如果您运行的是 Windows,则需要将以下几行放入 C:\autoexec.bat 文件中。

set CATALINA = C:\apache-tomcat-8.0.28
set CLASSPATH = %CATALINA%\common\lib\servlet-api.jar;%CLASSPATH%

或者,在 Windows NT/2000/XP 上,您可以转到"我的电脑"−>"属性"−>"高级"−>"环境变量"。然后,您可以更新 CLASSPATH 值并按"确定"按钮。

在 Unix(Solaris、Linux 等)上,如果您使用的是 C shell,则应将以下几行放入 .cshrc 文件中。

setenv CATALINA = /usr/local/apache-tomcat-8.0.28
setenv CLASSPATH $CATALINA/common/lib/servlet-api.jar:$CLASSPATH

注意 − 假设您的开发目录是 C:\ServletDevel(Windows)或 /usr/ServletDevel(Unix),那么您也需要以与上面类似的方式在 CLASSPATH 中添加这些目录。

Servlet - 生命周期

Servlet 生命周期可以定义为从创建到销毁的整个过程。以下是 servlet 所遵循的路径。

  • 通过调用 init() 方法来初始化 servlet。

  • Servlet 调用 service() 方法来处理客户端的请求。

  • 通过调用 destroy() 方法来终止 servlet。

  • 最后,Servlet 由 JVM 的垃圾收集器进行垃圾收集。

现在让我们详细讨论生命周期方法。

init() 方法

init 方法仅被调用一次。它仅在创建 servlet 时调用,之后不会为任何用户请求调用。因此,它用于一次性初始化,就像小程序的 init 方法一样。

通常在用户首次调用与 servlet 对应的 URL 时创建 servlet,但您也可以指定在服务器首次启动时加载 servlet。

当用户调用 servlet 时,将创建每个 servlet 的单个实例,每个用户请求都会产生一个新线程,该线程将根据需要传递给 doGet 或 doPost。init() 方法只是创建或加载一些将在 servlet 的整个生命周期中使用的数据。

init 方法定义如下所示 −

public void init() throws ServletException {
   // Initialization code...
}

service() 方法

service() 方法是执行实际任务的主要方法。servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求并将格式化的响应写回客户端。

每次服务器收到 servlet 请求时,服务器都会生成一个新线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等)并根据需要调用 doGet、doPost、doPut、doDelete 等方法。

以下是此方法的签名 −

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

service () 方法由容器调用,service 方法会根据需要调用 doGet、doPost、doPut、doDelete 等方法。因此,您无需处理 service() 方法,但可以根据从客户端收到的请求类型覆盖 doGet() 或 doPost()。

doGet() 和 doPost() 是每个服务请求中最常用的方法。以下是这两个方法的签名。

doGet() 方法

GET 请求来自对 URL 的正常请求或未指定 METHOD 的 HTML 表单,应由 doGet() 方法处理。

public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
   // Servlet code
}

doPost() 方法

POST 请求来自 HTML 表单,该表单明确将 POST 列为方法,并且应由 doPost() 方法处理。

public void doPost(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
   // Servlet code
}

destroy() 方法

destroy() 方法仅在 servlet 生命周期结束时调用一次。此方法使您的 servlet 有机会关闭数据库连接、停止后台线程、将 cookie 列表或命中计数写入磁盘以及执行其他此类清理活动。

调用 destroy() 方法后,servlet 对象将被标记为垃圾回收。destroy 方法定义如下 −

public void destroy() {
   // Finalization code...
}

架构图

下图描述了典型的 servlet 生命周期场景。

  • 首先,到达服务器的 HTTP 请求被委托给 servlet 容器。

  • servlet 容器在调用 service() 方法之前加载 servlet。

  • 然后 servlet 容器通过生成多个线程来处理多个请求,每个线程执行 servlet 单个实例的 service() 方法。

Servlet 生命周期

Servlet - 示例

Servlet 是 Java 类,用于服务 HTTP 请求并实现 javax.servlet.Servlet 接口。Web 应用程序开发人员通常编写扩展 javax.servlet.http.HttpServlet 的 servlet,javax.servlet.http.HttpServlet 是实现 Servlet 接口的抽象类,专门用于处理 HTTP 请求。

示例代码

以下是 servlet 示例的源代码结构示例,用于展示 Hello World −

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// 扩展 HttpServlet 类
public class HelloWorld extends HttpServlet {
 
   private String message;

   public void init() throws ServletException {
      // 进行必要的初始化
      message = "Hello World";
   }

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");

      // 实际逻辑在这里。
      PrintWriter out = response.getWriter();
      out.println("<h1>" + message + "</h1>");
   }

   public void destroy() {
      // do nothing.
   }
}

编译 Servlet

让我们使用上面显示的代码创建一个名为 HelloWorld.java 的文件。将此文件放在 C:\ServletDevel(在 Windows 中)或 /usr/ServletDevel(在 Unix 中)。必须将此路径位置添加到 CLASSPATH 才能继续。

假设您的环境已正确设置,请进入 ServletDevel 目录并按如下方式编译 HelloWorld.java −

$ javac HelloWorld.java

如果 servlet 依赖于任何其他库,您还必须在 CLASSPATH 中包含这些 JAR 文件。我只包含了 servlet-api.jar JAR 文件,因为我在 Hello World 程序中没有使用任何其他库。

此命令行使用 Sun Microsystems Java 软件开发工具包 (JDK) 附带的内置 javac 编译器。为了使此命令正常工作,您必须在 PATH 环境变量中包含您正在使用的 Java SDK 的位置。

如果一切顺利,上述编译将在同一目录中生成 HelloWorld.class 文件。下一节将解释如何在生产中部署已编译的 servlet。

Servlet 部署

默认情况下,servlet 应用程序位于路径 <Tomcat-installationdirectory>/webapps/ROOT,类文件将驻留在 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中。

如果您有一个完全限定的类名 com.myorg.MyServlet,那么这个 servlet 类必须位于 WEB-INF/classes/com/myorg/MyServlet.class 中。

现在,让我们将 HelloWorld.class 复制到 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中,并在位于的 web.xml 文件中创建以下条目/webapps/ROOT/WEB-INF/

<servlet>
   <servlet-name>HelloWorld</servlet-name>
   <servlet-class>HelloWorld</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>HelloWorld</servlet-name>
   <url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>

上述条目将在 web.xml 文件中可用的 <web-app>...</web-app> 标记内创建。此表中可能已存在各种条目,但没关系。

您几乎已完成,现在让我们使用 <Tomcat-installationdirectory>\bin\startup.bat(在 Windows 上)或 <Tomcat-installationdirectory>/bin/startup.sh(在 Linux/Solaris 等上)启动 tomcat 服务器,最后在浏览器的地址框中输入 http://localhost:8080/HelloWorld。如果一切顺利,您将获得以下结果

Servlet 示例

Servlets - 表单数据

您一定遇到过许多需要将一些信息从浏览器传递到 Web 服务器并最终传递到后端程序的情况。浏览器使用两种方法将这些信息传递给 Web 服务器。这些方法是 GET 方法和 POST 方法。

GET 方法

GET 方法将编码的用户信息附加到页面请求中发送。页面和编码的信息由 ?(问号)符号分隔,如下所示 −

http://www.test.com/hello?key1 = value1&key2 = value2

GET 方法是将信息从浏览器传递到 Web 服务器的默认方法,它会生成一个长字符串,该字符串显示在浏览器的 Location:box 中。如果您有密码或其他敏感信息要传递给服务器,切勿使用 GET 方法。 GET 方法有大小限制:请求字符串中只能使用 1024 个字符。

此信息使用 QUERY_STRING 标头传递,可通过 QUERY_STRING 环境变量访问,Servlet 使用 doGet() 方法处理此类请求。

POST 方法

将信息传递给后端程序的更可靠方法是 POST 方法。该方法以与 GET 方法完全相同的方式打包信息,但它不是将其作为 URL 中 ?(问号)后的文本字符串发送,而是将其作为单独的消息发送。此消息以标准输入的形式发送到后端程序,您可以解析并用于处理。 Servlet 使用 doPost() 方法处理此类请求。

使用 Servlet 读取表单数据

Servlet 根据情况使用以下方法自动处理表单数据解析 −

  • getParameter() − 调用 request.getParameter() 方法获取表单参数的值。

  • getParameterValues() − 如果参数出现多次并返回多个值(例如复选框),则调用此方法。

  • getParameterNames() −如果您想要当前请求中所有参数的完整列表,请调用此方法。

使用 URL 的 GET 方法示例

这是一个简单的 URL,它将使用 GET 方法将两个值传递给 HelloForm 程序。

http://localhost:8080/HelloForm?first_name = ZARA&last_name = ALI

下面给出的是 HelloForm.java servlet 程序,用于处理 Web 浏览器提供的输入。我们将使用 getParameter() 方法,该方法可以非常轻松地访问传递的信息 −

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// 扩展 HttpServlet 类
public class HelloForm extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Using GET Method to Read Form Data";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
         
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + title + "</h1>" ++
               "<ul>" ++
                  "  <li><b>First Name</b>: "
                  + request.getParameter("first_name") + "" ++
                  "  <li><b>Last Name</b>: "
                  + request.getParameter("last_name") + "" ++
               "</ul>" ++
            "</body>" +
         "</html>"
      );
   }
}

假设您的环境已正确设置,请按如下方式编译 HelloForm.java −

$ javac HelloForm.java

如果一切顺利,上述编译将生成 HelloForm.class 文件。接下来,您必须将此类文件复制到 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中,并在位于 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/ 的 web.xml 文件中创建以下条目

<servlet>
   <servlet-name>HelloForm</servlet-name>
   <servlet-class>HelloForm</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>HelloForm</servlet-name>
   <url-pattern>/HelloForm</url-pattern>
</servlet-mapping>

现在在浏览器的 Location:box 中输入 http://localhost:8080/HelloForm?first_name=ZARA&last_name=ALI,并确保在浏览器中执行上述命令之前已启动 tomcat 服务器。这将生成以下结果 −

使用 GET 方法读取表单数据

  • First Name: ZARA
  • Last Name: ALI

使用表单的 GET 方法示例

这是一个使用 HTML 表单和提交按钮传递两个值的简单示例。我们将使用相同的 Servlet HelloForm 来处理此输入。

<html>
   <body>
      <form action = "HelloForm" method = "GET">
         First Name: <input type = "text" name = "first_name">
         <br />
         Last Name: <input type = "text" name = "last_name" />
         <input type = "submit" value = "Submit" />
      </form>
   </body>
</html>

将此 HTML 保存在文件 Hello.htm 中,并将其放在 <Tomcat-installationdirectory>/webapps/ROOT 目录中。当您访问 http://localhost:8080/Hello.htm 时,以下是上述表单的实际输出。

First Name: Last Name:

尝试输入名字和姓氏,然后单击提交按钮以在运行 tomcat 的本地计算机上查看结果。根据提供的输入,它将生成与上述示例中提到的类似的结果。

使用表单的 POST 方法示例

让我们对上面的 servlet 进行一些修改,以便它可以处理 GET 和 POST 方法。以下是 HelloForm.java servlet 程序,用于使用 GET 或 POST 方法处理 Web 浏览器提供的输入。

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// 扩展 HttpServlet 类
public class HelloForm extends HttpServlet {

   // 处理 GET 方法请求的方法。
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Using GET Method to Read Form Data";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " +
         "transitional//en\">";
         
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + title + "</h1>" ++
               "<ul>" ++
                  "  <li><b>First Name</b>: "
                  + request.getParameter("first_name") + "" ++
                  "  <li><b>Last Name</b>: "
                  + request.getParameter("last_name") + "" ++
               "</ul>" ++
            "</body>"
         "</html>"
      );
   }

   // 处理 POST 方法请求的方法。
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      doGet(request, response);
   }
}

现在编译并部署上述 Servlet,并使用 Hello.htm 以 POST 方法进行测试,如下所示 −

<html>
   <body>
      <form action = "HelloForm" method = "POST">
         First Name: <input type = "text" name = "first_name">
         <br />
         Last Name: <input type = "text" name = "last_name" />
         <input type = "submit" value = "Submit" />
      </form>
   </body>
</html>

这是上述表单的实际输出,尝试输入名字和姓氏,然后单击提交按钮以在运行 tomcat 的本地计算机上查看结果。

First Name: Last Name:

根据提供的输入,它将生成与上述示例中提到的类似的结果。

将复选框数据传递给 Servlet 程序

当需要选择多个选项时,使用复选框。

以下是带有两个复选框的表单的示例 HTML 代码 CheckBox.htm

<html>
   <body>
      <form action = "CheckBox" method = "POST" target = "_blank">
         <input type = "checkbox" name = "maths" checked = "checked" /> Maths
         <input type = "checkbox" name = "physics"  /> Physics
         <input type = "checkbox" name = "chemistry" checked = "checked" /> 
                                          Chemistry
         <input type = "submit" value = "Select Subject" />
      </form>
   </body>
</html>

此代码的结果如下

Maths Physics Chemistry

下面给出的是 CheckBox.java servlet 程序,用于处理 Web 浏览器为复选框按钮提供的输入。

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// 扩展 HttpServlet 类
public class CheckBox extends HttpServlet {
 
   // 处理 GET 方法请求的方法。
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Reading Checkbox Data";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";

      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + title + "</h1>" ++
               "<ul>" ++
                  "  <li><b>Maths Flag : </b>: "
                  + request.getParameter("maths") + "" ++
                  "  <li><b>Physics Flag: </b>: "
                  + request.getParameter("physics") + "" ++
                  "  <li><b>Chemistry Flag: </b>: "
                  + request.getParameter("chemistry") + "" ++
               "</ul>" ++
            "</body>"
         "</html>"
      );
   }

   // 处理 POST 方法请求的方法。
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

对于上述示例,它将显示以下结果 −

读取复选框数据

  • Maths Flag : : on
  • Physics Flag: : null
  • Chemistry Flag: : on

读取所有表单参数

以下是使用 HttpServletRequest 的 getParameterNames() 方法读取所有可用表单参数的通用示例。此方法返回一个枚举,其中包含未指定顺序的参数名称

一旦我们有了枚举,我们就可以以标准方式循环枚举,方法是使用 hasMoreElements() 方法确定何时停止,并使用 nextElement() 方法获取每个参数名称。

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// 扩展 HttpServlet 类
public class ReadParams extends HttpServlet {
 
   // 处理 GET 方法请求的方法。
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Reading All Form Parameters";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";

      out.println(docType +
         "<html>" ++
         "<head><title>" + title + "</title></head>" ++
         "<body bgcolor = \"#f0f0f0\">" ++
         "<h1 align = \"center\">" + title + "</h1>" ++
         "<table width = \"100%\" border = \"1\" align = \"center\">" ++
         "<tr bgcolor = \"#949494\">" ++
            "<th>Param Name</th>"
            "<th>Param Value(s)</th>
"+
         "</tr>
"
      );

      Enumeration paramNames = request.getParameterNames();

      while(paramNames.hasMoreElements()) {
         String paramName = (String)paramNames.nextElement();
         out.print("<tr><td>" + paramName + "</td>
<td>");
         String[] paramValues = request.getParameterValues(paramName);

         // 读取单值数据
         if (paramValues.length == 1) {
            String paramValue = paramValues[0];
            if (paramValue.length() == 0)
               out.println("<i>No Value</i>");
               else
               out.println(paramValue);
         } else {
            // 读取多值数据
            out.println("<ul>");

            for(int i = 0; i < paramValues.length; i++) {
               out.println("<li>" + paramValues[i]);
            }
            out.println("</ul>");
         }
      }
      out.println("</tr>
</table>
</body></html>");
   }
   
   // 处理 POST 方法请求的方法。
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

现在,使用以下形式尝试上述 servlet −

<html>
   <body>
      <form action = "ReadParams" method = "POST" target = "_blank">
         <input type = "checkbox" name = "maths" checked = "checked" /> Maths
         <input type = "checkbox" name = "physics"  /> Physics
         <input type = "checkbox" name = "chemistry" checked = "checked" /> Chem
         <input type = "submit" value = "Select Subject" />
      </form>
   </body>
</html>

现在使用上述表单调用 servlet 将生成以下结果 −

读取所有表单参数

参数名称 参数值
maths on
chemistry on

您可以尝试上述 servlet 来读取具有其他对象(如文本框、单选按钮或下拉框等)的任何其他表单的数据。

Servlets - 客户端 HTTP 请求

当浏览器请求网页时,它会向 Web 服务器发送大量信息,这些信息无法直接读取,因为这些信息作为 HTTP 请求标头的一部分传输。您可以查看 HTTP 协议 以了解更多信息。

以下是来自浏览器端的重要标头信息,在 Web 编程中会经常使用 −

Sr.No. 标头 &描述
1

Accept

此标头指定浏览器或其他客户端可以处理的 MIME 类型。image/pngimage/jpeg 值是两种最常见的可能性。

2

Accept-Charset

此标头指定浏览器可用于显示信息的字符集。例如 ISO-8859-1。

3

Accept-Encoding

此标头指定浏览器知道如何处理的编码类型。gzipcompress 值是两种最常见的可能性。

4

Accept-Language

此标头指定客户端的首选语言,以防 servlet 可以生成多种语言的结果。例如 en、en-us、ru 等

5

授权

客户端在访问受密码保护的网页时使用此标头来标识自己。

6

连接

此标头指示客户端是否可以处理持久 HTTP 连接。持久连接允许客户端或其他浏览器通过单个请求检索多个文件。 Keep-Alive 的值表示应使用持久连接。

7

Content-Length

此标头仅适用于 POST 请求,并以字节为单位提供 POST 数据的大小。

8

Cookie

此标头将 cookie 返回到先前将其发送到浏览器的服务器。

9

Host

此标头指定原始 URL 中给出的主机和端口。

10

If-Modified-Since

此标头表示客户端仅在指定日期后更改页面时才需要该页面。如果没有更新的结果,服务器将发送代码 304,表示 Not Modified 标头。

11

If-Unmodified-Since

此标头与 If-Modified-Since 相反;它指定仅当文档早于指定日期时,操作才会成功。

12

Referer

此标头指示引用网页的 URL。例如,如果您在网页 1 上单击了网页 2 的链接,则当浏览器请求网页 2 时,网页 1 的 URL 会包含在 Referrer 标头中。

13

User-Agent

此标头标识发出请求的浏览器或其他客户端,可用于向不同类型的浏览器返回不同的内容。

读取 HTTP 标头的方法

有以下方法可用于在 servlet 程序中读取 HTTP 标头。这些方法可通过 HttpServletRequest 对象使用

Sr.No. 方法 &描述
1

Cookie[] getCookies()

返回一个数组,其中包含客户端随此请求发送的所有 Cookie 对象。

2

Enumeration getAttributeNames()

返回一个枚举,其中包含此请求可用的属性名称。

3

Enumeration getHeaderNames()

返回此请求的所有标头名称的枚举包含。

4

Enumeration getParameterNames()

返回包含此请求中所含参数名称的字符串对象枚举

5

HttpSession getSession()

返回与此请求关联的当前会话,如果请求没有会话,则创建一个会话。

6

HttpSession getSession(boolean create)

返回与此请求关联的当前 HttpSession,如果没有当前会话且 create 的值为 true,则返回一个新会话。

7

Locale getLocale()

根据 Accept-Language 标头,返回客户端将接受内容的首选语言环境。

8

Object getAttribute(String name)

以 Object 形式返回命名属性的值,如果不存在给定名称的属性,则返回 null。

9

ServletInputStream getInputStream()

使用 ServletInputStream 将请求主体作为二进制数据检索。

10

String getAuthType()

返回用于保护 servlet 的身份验证方案的名称,例如"BASIC"或"SSL",如果 JSP 未受保护,则返回 null。

11

String getCharacterEncoding()

返回此请求正文中使用的字符编码的名称。

12

String getContentType()

返回请求正文的 MIME 类型,如果类型未知,则返回 null。

13

String getContextPath()

返回请求 URI 中指示请求上下文的部分。

14

String getHeader(String name)

以字符串形式返回指定请求标头的值。

15

String getMethod()

返回发出此请求的 HTTP 方法的名称,例如 GET、POST 或 PUT。

16

String getParameter(String name)

以字符串形式返回请求参数的值,如果参数不存在则返回 null。

17

String getPathInfo()

返回与客户端发出此请求时发送的 URL 相关的任何额外路径信息

18

String getProtocol()

返回请求协议的名称和版本。

19

String getQueryString()

返回请求 URL 中路径后的查询字符串。

20

String getRemoteAddr()

返回发送请求的客户端的 Internet 协议 (IP) 地址。

21

String getRemoteHost()

返回发送请求的客户端的完全限定名称。

22

String getRemoteUser()

如果用户已经通过身份验证,则返回发出此请求的用户的登录信息,如果用户尚未通过身份验证,则返回 null。

23

String getRequestURI()

返回此请求的 URL 中从协议名称到 HTTP 请求第一行中的查询字符串的部分。

24

String getRequestedSessionId()

返回客户端指定的会话 ID。

25

String getServletPath()

返回此请求的 URL 中调用 JSP 的部分。

26

String[] getParameterValues(String name)

返回包含给定请求参数的所有值的 String 对象数组,如果参数不存在则返回 null。

27

boolean isSecure()

返回一个布尔值,指示此请求是否使用安全通道(如 HTTPS)发出。

28

int getContentLength()

返回请求主体的长度(以字节为单位),由输入流提供,如果长度未知则返回 -1。

29

int getIntHeader(String name)

以 int 形式返回指定请求标头的值。

30

int getServerPort()

返回接收此请求的端口号。

HTTP 标头请求示例

以下是使用 HttpServletRequest 的 getHeaderNames() 方法读取 HTTP 标头信息的示例。此方法返回一个枚举,其中包含与当前 HTTP 请求相关的标头信息。

一旦我们有了枚举,我们就可以以标准方式循环枚举,使用 hasMoreElements() 方法确定何时停止,并使用 nextElement() 方法获取每个参数名称

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
 
// 扩展 HttpServlet 类
public class DisplayHeader extends HttpServlet {
 
   // 处理 GET 方法请求的方法。
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");
 
      PrintWriter out = response.getWriter();
      String title = "HTTP Header Request Example";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";

      out.println(docType +
         "<html>" ++
         "<head><title>" + title + "</title></head>
"+
         "<body bgcolor = \"#f0f0f0\">" ++
         "<h1 align = \"center\">" + title + "</h1>" ++
         "<table width = \"100%\" border = \"1\" align = \"center\">" ++
         "<tr bgcolor = \"#949494\">" ++
         "<th>Header Name</th><th>Header Value(s)</th>
"+
         "</tr>
"
      );
 
      Enumeration headerNames = request.getHeaderNames();
    
      while(headerNames.hasMoreElements()) {
         String paramName = (String)headerNames.nextElement();
         out.print("<tr><td>" + paramName + "</td>
");
         String paramValue = request.getHeader(paramName);
         out.println("<td> " + paramValue + "</td></tr>
");
      }
      out.println("</table>
</body></html>");
   }
   
   // 处理 POST 方法请求的方法。
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      doGet(request, response);
   }
}

现在调用上述 servlet 将生成以下结果 −

HTTP 标头请求示例

标头名称 标头值
accept */*
accept-language en-us
user-agent Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; InfoPath.2; MS-RTC LM 8)
accept-encoding gzip, deflate
host localhost:8080
connection Keep-Alive
cache-control no-cache

Servlet - 服务器 HTTP 响应

如上一章所述,当 Web 服务器响应 HTTP 请求时,响应通常由状态行、一些响应标头、空白行和文档组成。典型的响应如下所示 −

HTTP/1.1 200 OK
Content-Type: text/html
Header2: ...
...
HeaderN: ...
   (Blank Line)
<!doctype ...>
<html>
   <head>...</head>
   <body>
      ...
   </body>
</html>

状态行由 HTTP 版本(示例中为 HTTP/1.1)、状态代码(示例中为 200)以及与状态代码相对应的极短消息(示例中为 OK)组成。

以下总结了最有用的 HTTP 1.1 响应标头,这些标头从 Web 服务器端返回到浏览器,在 Web 编程中您会非常频繁地使用它们 −

Sr.No. 标头和说明
1

Allow

此标头指定服务器支持的请求方法(GET、POST 等)。

2

Cache-Control

此标头指定响应文档可以安全缓存的情况。它可以具有值publicprivateno-cache等。Public 表示文档可缓存,Private 表示文档仅供单个用户使用,并且只能存储在私有(非共享)缓存中,nocache 表示文档永远不应被缓存。

3

Connection

此标头指示浏览器是否在 HTTP 连接中使用持久连接。 close 值指示浏览器不使用持久 HTTP 连接,而 keepalive 表示使用持久连接。

4

Content-Disposition

此标头允许您请求浏览器要求用户将响应保存到磁盘中给定名称的文件中。

5

Content-Encoding

此标头指定页面在传输过程中的编码方式。

6

Content-Language

此标头表示文档所用的语言。例如 en、en-us、ru 等

7

Content-Length

此标头表示响应中的字节数。仅当浏览器使用持久(保持活动)HTTP 连接时才需要此信息。

8

Content-Type

此标头提供响应文档的 MIME(多用途互联网邮件扩展)类型。

9

Expires

此标头指定内容应被视为过期并因此不再缓存的时间。

10

Last-Modified

此标头指示文档的最后更改时间。然后,客户端可以缓存文档,并在以后的请求中通过 If-Modified-Since 请求标头提供日期。

11

Location

此标头应包含在所有具有 300 秒内状态代码的响应中。这会将文档地址通知浏览器。浏览器会自动重新连接到此位置并检索新文档。

12

Refresh

此标头指定浏览器应多久请求一次更新页面。您可以指定页面刷新后的时间(以秒为单位)。

13

Retry-After

此标头可与 503(服务不可用)响应结合使用,以告知客户端多久可以重复其请求。

14

Set-Cookie

此标头指定与页面关联的 cookie。

设置 HTTP 响应标头的方法

有以下方法可用于在 servlet 程序中设置 HTTP 响应标头。这些方法可用于 HttpServletResponse 对象。

Sr.No. 方法 &描述
1

String encodeRedirectURL(String url)

对指定的 URL 进行编码以用于 sendRedirect 方法,如果不需要编码,则返回未更改的 URL。

2

String encodeURL(String url)

通过在其中包含会话 ID 来对指定的 URL 进行编码,如果不需要编码,则返回未更改的 URL。

3

boolean containsHeader(String name)

返回一个布尔值,指示命名的响应标头是否已设置。

4

boolean isCommitted()

返回一个布尔值,指示响应是否已提交。

5

void addCookie(Cookie cookie)

将指定的 cookie 添加到响应中。

6

void addDateHeader(String name, long date)

添加具有给定名称和日期值的响应标头。

7

void addHeader(String name, String value)

添加具有给定名称和值的响应标头。

8

void addIntHeader(String name, int value)

添加具有给定名称和整数值的响应标头。

9

void flushBuffer()

强制将缓冲区中的任何内容写入客户端。

10

void reset()

清除缓冲区中存在的任何数据以及状态代码和标头。

11

void resetBuffer()

清除响应中底层缓冲区的内容,但不清除标头或状态代码。

12

void sendError(int sc)

使用指定的状态代码向客户端发送错误响应并清除缓冲区。

13

void sendError(int sc, String msg)

使用指定的状态向客户端发送错误响应。

14

void sendRedirect(String location)

使用指定的重定向位置 URL 向客户端发送临时重定向响应。

15

void setBufferSize(int size)

设置响应主体的首选缓冲区大小。

16

void setCharacterEncoding(String charset)

设置发送给客户端的响应的字符编码(MIME 字符集),例如,设置为 UTF-8。

17

void setContentLength(int len)

设置响应中内容主体的长度 在 HTTP servlet 中,此方法设置 HTTP Content-Length 标头。

18

void setContentType(String type)

如果响应尚未提交,则设置发送给客户端的响应的内容类型。

19

void setDateHeader(String name, long date)

使用给定的名称和日期值设置响应标头。

20

void setHeader(String name, String value)

使用给定的名称和值设置响应标头。

21

void setIntHeader(String name, int value)

使用给定的名称和整数值设置响应标头

22

void setLocale(Locale loc)

如果响应尚未提交,则设置响应的语言环境。

23

void setStatus(int sc)

设置此响应的状态代码

HTTP 标头响应示例

您已经在前面的示例中看到了 setContentType() 方法的工作原理,下面的示例也将使用相同的方法,此外,我们将使用 setIntHeader() 方法来设置 Refresh 标头。

// 导入所需的 java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
 
// 扩展 HttpServlet 类
public class Refresh extends HttpServlet {
 
   // 处理 GET 方法请求的方法。
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   
      // 设置刷新、自动加载时间为 5 秒
      response.setIntHeader("Refresh", 5);
 
      // 设置响应内容类型
      response.setContentType("text/html");
 
      // 获取当前时间
      Calendar calendar = new GregorianCalendar();
      String am_pm;
      int hour = calendar.get(Calendar.HOUR);
      int minute = calendar.get(Calendar.MINUTE);
      int second = calendar.get(Calendar.SECOND);
         
      if(calendar.get(Calendar.AM_PM) == 0)
         am_pm = "AM";
      else
         am_pm = "PM";
 
      String CT = hour+":"+ minute +":"+ second +" "+ am_pm;
    
      PrintWriter out = response.getWriter();
      String title = "Auto Refresh Header Setting";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";

      out.println(docType +
         "<html>" ++
         "<head><title>" + title + "</title></head>"+
         "<body bgcolor = \"#f0f0f0\">" ++
         "<h1 align = \"center\">" + title + "</h1>" ++
         "<p>Current Time is: " + CT + "</p>"
      );
   }
   
   // 处理 POST 方法请求的方法。
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

现在调用上述 servlet 将每 5 秒显示一次当前系统时间,如下所示。只需运行 servlet 并等待查看结果 −

Auto Refresh Header Setting

Current Time is: 9:44:50 PM

Servlets - Http 状态代码

HTTP 请求和 HTTP 响应消息的格式类似,将具有以下结构 −

  • 初始状态行 + CRLF(回车符 + 换行符,即新行)

  • 零个或多个标题行 + CRLF

  • 空白行,即 CRLF

  • 可选消息正文,如文件、查询数据或查询输出。

例如,服务器响应标头如下所示 −

HTTP/1.1 200 OK
Content-Type: text/html
Header2: ...
...
HeaderN: ...
   (Blank Line)
<!doctype ...>
<html>
   <head>...</head>
   <body>
      ...
   </body>
</html>

状态行由 HTTP 版本(示例中为 HTTP/1.1)、状态代码(示例中为 200)以及与状态代码对应的非常短的消息(示例中为 OK)组成。

以下是可能从 Web 服务器返回的 HTTP 状态代码和相关消息的列表 −

Code Message Description
100 Continue 服务器只收到了部分请求,但只要没有被拒绝,客户端就应该继续该请求
101 Switching Protocols 服务器切换协议。
200 OK 请求成功
201 Created 请求完成,新资源被创建
202 Accepted 请求已被接受进行处理,但处理尚未完成。
203 Non-authoritative Information  
204 No Content  
205 Reset Content  
206 Partial Content  
300 Multiple Choices 链接列表。用户可以选择一个链接并转到该位置。最多五个地址
301 Moved Permanently 请求的页面已移至新的 url
302 Found 请求的页面已暂时移至新的 URL
303 See Other 所请求的页面可以在不同的 URL 下找到
304 Not Modified  
305 Use Proxy  
306 Unused 此代码在之前的版本中使用过。现在不再使用,但保留此代码
307 Temporary Redirect 请求的页面已暂时移至新的 URL。
400 Bad Request 服务器无法理解该请求
401 Unauthorized 请求的页面需要用户名和密码
402 Payment Required 您还不能使用此代码
403 Forbidden 禁止访问所请求的页面
404 Not Found 服务器无法找到所请求的页面。
405 Method Not Allowed 请求中指定的方法不被允许。
406 Not Acceptable 服务器只能生成不被客户端接受的响应。
407 Proxy Authentication Required 您必须先通过代理服务器进行身份验证,然后才能处理此请求。
408 Request Timeout 请求所花的时间超出了服务器准备等待的时间。
409 Conflict 由于冲突,请求无法完成。
410 Gone 所请求的页面不再可用。
411 Length Required "Content-Length"未定义。如果没有它,服务器将不会接受请求。
412 Precondition Failed 请求中给出的先决条件被服务器评估为 false。
413 Request Entity Too Large 服务器不会接受该请求,因为请求实体太大。
414 Request-url Too Long 服务器不会接受请求,因为 URL 太长。当您将"post"请求转换为带有长查询信息的"get"请求时发生。
415 Unsupported Media Type 服务器不会接受该请求,因为不支持该媒体类型。
417 Expectation Failed  
500 Internal Server Error 请求未完成。服务器遇到意外情况。
501 Not Implemented 请求未完成。服务器不支持所需的功能。
502 Bad Gateway 请求未完成。服务器从上游服务器收到无效响应。
503 Service Unavailable 请求未完成。服务器暂时超载或宕机。
504 Gateway Timeout 网关已超时。
505 HTTP Version Not Supported 服务器不支持"http 协议"版本。

设置 HTTP 状态代码的方法

以下方法可用于在 servlet 程序中设置 HTTP 状态代码。这些方法可用于 HttpServletResponse 对象。

Sr.No. 方法和说明
1

public void setStatus ( int statusCode )

此方法设置任意状态代码。 setStatus 方法将 int(状态代码)作为参数。如果您的响应包含特殊状态代码和文档,请确保在使用 PrintWriter 实际返回任何内容之前调用 setStatus。

2

public void sendRedirect(String url)

此方法生成 302 响应以及提供新文档 URL 的 Location 标头

3

public void sendError(int code, String message)

此方法发送状态代码(通常为 404)以及在 HTML 文档中自动格式化并发送到客户端的短消息。

HTTP 状态代码示例

以下示例将发送407 错误代码发送到客户端浏览器,浏览器会显示"需要身份验证!!!"消息。

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// 扩展 HttpServlet 类
public class showError extends HttpServlet {
 
   // 处理 GET 方法请求的方法。
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置错误代码和原因。
      response.sendError(407, "Need authentication!!!" );
   }
   
   // 处理 POST 方法请求的方法。
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

现在调用上述 servlet 将显示以下结果 −

HTTP Status 407 - Need authentication!!!

type Status report

messageNeed authentication!!!

descriptionThe client must first authenticate itself with the proxy (Need authentication!!!).

Apache Tomcat/5.5.29

Servlet - 编写过滤器

Servlet 过滤器是 Java 类,可用于 Servlet 编程,目的如下 −

  • 在客户端访问后端资源之前拦截来自客户端的请求。

  • 在服务器响应发送回客户端之前对其进行处理。

规范建议使用各种类型的过滤器 −

  • 身份验证过滤器。
  • 数据压缩过滤器。
  • 加密过滤器。
  • 触发资源访问事件的过滤器。
  • 图像转换过滤器。
  • 日志记录和审计过滤器。
  • MIME-TYPE 链过滤器。
  • 标记化过滤器。
  • 转换 XML 内容的 XSL/T 过滤器。

过滤器部署在部署描述符文件 web.xml 中,然后映射到应用程序部署描述符中的 servlet 名称或 URL 模式。

当 Web 容器启动您的 Web 应用程序时,它会为您在部署描述符中声明的每个过滤器创建一个实例。过滤器按照部署描述符中声明的顺序执行。

Servlet 过滤器方法

过滤器只是一个实现 javax.servlet.Filter 接口的 Java 类。javax.servlet.Filter 接口定义了三种方法 −

Sr.No. 方法 &描述
1

public void doFilter (ServletRequest, ServletResponse, FilterChain)

每次由于客户端请求链末端的资源而使请求/响应对通过链时,容器都会调用此方法。

2

public void init(FilterConfig filterConfig)

Web 容器调用此方法以向过滤器指示它已投入服务。

3

public void destroy()

此方法由 Web 容器调用,以向过滤器指示其已停止服务。

Servlet 过滤器 − 示例

以下是 Servlet 过滤器示例,它将打印客户端 IP 地址和当前日期时间。此示例将让您对 Servlet 过滤器有基本的了解,但您可以使用相同的概念编写更复杂的过滤器应用程序 −

// 导入所需的 java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// 实现 Filter 类
public class LogFilter implements Filter  {
   public void  init(FilterConfig config) throws ServletException {
      
    // 获取初始化参数
    String testParam = config.getInitParameter("test-param");
    
    //打印初始化参数
    System.out.println("Test Param: " + testParam); 
   }
   
   public void  doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws java.io.IOException, ServletException {

        // 获取客户端计算机的 IP 地址。
        String ipAddress = request.getRemoteAddr();
        
        // 记录 IP 地址和当前时间戳。
        System.out.println("IP "+ ipAddress + ", Time " + new Date().toString());
        
        // 将请求传递回过滤器链
        chain.doFilter(request,response);
   }

   public void destroy( ) {
      /* 在 Web 容器从服务中删除 Filter 实例之前调用*/
   }
}

以通常的方式编译 LogFilter.java,并将类文件放入 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes

Web.xml 中的 Servlet 过滤器映射

过滤器定义后映射到 URL 或 Servlet,其定义方式与 Servlet 定义后映射到 URL 模式的方式非常相似。在部署描述符文件 web.xml

中为过滤器标记创建以下条目
<filter>
   <filter-name>LogFilter</filter-name>
   <filter-class>LogFilter</filter-class>
   <init-param>
      <param-name>test-param</param-name>
      <param-value>Initialization Paramter</param-value>
   </init-param>
</filter>

<filter-mapping>
   <filter-name>LogFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

上述过滤器将应用于所有 servlet,因为我们在配置中指定了 /*。如果您只想对少数 servlet 应用过滤器,则可以指定特定的 servlet 路径。

现在尝试以通常的方式调用任何 servlet,您将在 Web 服务器日志中看到生成的日志。您可以使用 Log4J 记录器将上述日志记录在单独的文件中。

使用多个过滤器

您的 Web 应用程序可能会定义几个具有特定用途的不同过滤器。考虑一下,您定义了两个过滤器 AuthenFilterLogFilter。其余过程将保持如上所述,但您需要创建不同的映射,如下所述 −

<filter>
   <filter-name>LogFilter</filter-name>
   <filter-class>LogFilter</filter-class>
   <init-param>
      <param-name>test-param</param-name>
      <param-value>Initialization Paramter</param-value>
   </init-param>
</filter>

<filter>
   <filter-name>AuthenFilter</filter-name>
   <filter-class>AuthenFilter</filter-class>
   <init-param>
      <param-name>test-param</param-name>
      <param-value>Initialization Paramter</param-value>
   </init-param>
</filter>

<filter-mapping>
   <filter-name>LogFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
   <filter-name>AuthenFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

过滤器应用顺序

web.xml 中的 filter-mapping 元素顺序决定了 Web 容器将过滤器应用于 servlet 的顺序。要反转过滤器的顺序,只需反转 web.xml 文件中的 filter-mapping 元素即可。

例如,上面的示例将首先应用 LogFilter,然后将 AuthenFilter 应用于任何 servlet,但下面的示例将反转顺序 −

<filter-mapping>
   <filter-name>AuthenFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
   <filter-name>LogFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

Servlet - 异常处理

当 servlet 抛出异常时,Web 容器会在 web.xml 中使用 exception-type 元素搜索与抛出的异常类型匹配的配置。

您必须使用 web.xml 中的 error-page 元素来指定对某些 异常 或 HTTP 状态代码 的 servlet 调用。

web.xml 配置

假设您有一个 ErrorHandler servlet,只要出现任何定义的异常或错误,就会调用该 servlet。以下是在 web.xml 中创建的条目。

<!-- servlet definition -->
<servlet>
   <servlet-name>ErrorHandler</servlet-name>
   <servlet-class>ErrorHandler</servlet-class>
</servlet>

<!-- servlet mappings -->
<servlet-mapping>
   <servlet-name>ErrorHandler</servlet-name>
   <url-pattern>/ErrorHandler</url-pattern>
</servlet-mapping>

<!-- error-code related error pages -->
<error-page>
   <error-code>404</error-code>
   <location>/ErrorHandler</location>
</error-page>

<error-page>
   <error-code>403</error-code>
   <location>/ErrorHandler</location>
</error-page>

<!-- exception-type related error pages -->
<error-page>
   <exception-type>
      javax.servlet.ServletException
   </exception-type >
   <location>/ErrorHandler</location>
</error-page>

<error-page>
   <exception-type>java.io.IOException</exception-type >
   <location>/ErrorHandler</location>
</error-page>

如果您想要为所有异常设置一个通用的错误处理程序,那么您应该定义以下错误页面,而不是为每个异常定义单独的错误页面元素 −

<error-page>
   <exception-type>java.lang.Throwable</exception-type >
   <location>/ErrorHandler</location>
</error-page>

以下是关于上述 web.xml 异常处理需要注意的几点 −

  • servlet ErrorHandler 的定义方式与任何其他 servlet 一样,并在 web.xml 中配置。

  • 如果出现任何错误,状态代码为 404(未找到)或 403(禁止访问),则会调用 ErrorHandler servlet。

  • 如果 Web 应用程序抛出 ServletException 或 IOException,则 Web 容器将调用 /ErrorHandler servlet。

  • 您可以定义不同的错误处理程序来处理不同类型的错误或异常。上面的例子非常通用,希望它能够帮助您解释基本概念。

请求属性 − 错误/异常

以下是错误处理 servlet 可以访问以分析错误/异常性质的请求属性列表。

Sr.No. 属性 &描述
1

javax.servlet.error.status_code

此属性提供状态代码,可在 java.lang.Integer 数据类型中存储和分析。

2

javax.servlet.error.exception_type

此属性提供有关异常类型的信息,可在 java.lang.Class 数据类型中存储和分析。

3

javax.servlet.error.message

此属性提供准确的错误消息,可在 java.lang.String 数据类型中存储和分析。

4

javax.servlet.error.request_uri

此属性提供有关调用 servlet 的 URL 的信息,可在 java.lang.String 数据类型中存储和分析。

5

javax.servlet.error.exception

此属性提供有关引发的异常的信息,可以存储和分析这些信息。

6

javax.servlet.error.servlet_name

此属性提供 servlet 名称,可以将其存储在 java.lang.String 数据类型中,然后进行存储和分析。

错误处理程序 Servlet 示例

此示例将让您对 Servlet 中的异常处理有基本的了解,但您可以使用相同的概念编写更复杂的过滤器应用程序 −

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// 扩展 HttpServlet 类
public class ErrorHandler extends HttpServlet {
 
   // 处理 GET 方法请求的方法。
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
         
      // Analyze the servlet exception       
      Throwable throwable = (Throwable)
      request.getAttribute("javax.servlet.error.exception");
      Integer statusCode = (Integer)
      request.getAttribute("javax.servlet.error.status_code");
      String servletName = (String)
      request.getAttribute("javax.servlet.error.servlet_name");
         
      if (servletName == null) {
         servletName = "Unknown";
      }
      String requestUri = (String)
      request.getAttribute("javax.servlet.error.request_uri");
      
      if (requestUri == null) {
         requestUri = "Unknown";
      }

      // 设置响应内容类型
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Error/Exception Information";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " +
         "transitional//en\">";
         
      out.println(docType +
         "<html>" ++
         "<head><title>" + title + "</title></head>" ++
         "<body bgcolor = \"#f0f0f0\">
");

      if (throwable == null && statusCode == null) {
         out.println("<h2>Error information is missing</h2>");
         out.println("Please return to the <a href=\"" + 
            response.encodeURL("http://localhost:8080/") + 
            "\">Home Page</a>.");
      } else if (statusCode != null) {
         out.println("The status code : " + statusCode);
      } else {
         out.println("<h2>Error information</h2>");
         out.println("Servlet Name : " + servletName + "</br></br>");
         out.println("Exception Type : " + throwable.getClass( ).getName( ) + "</br></br>");
         out.println("The request URI: " + requestUri + "<br><br>");
         out.println("The exception message: " + throwable.getMessage( ));
      }
      out.println("</body>");
      out.println("</html>");
   }
   
   // 处理 POST 方法请求的方法。
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

按通常方式编译 ErrorHandler.java,并将类文件放在 /webapps/ROOT/WEB-INF/classes 中。

让我们在 web.xml 中添加以下配置来处理异常 −

<servlet>
   <servlet-name>ErrorHandler</servlet-name>
   <servlet-class>ErrorHandler</servlet-class>
</servlet>

<!-- servlet mappings -->
<servlet-mapping>
   <servlet-name>ErrorHandler</servlet-name>
   <url-pattern>/ErrorHandler</url-pattern>
</servlet-mapping>

<error-page>
   <error-code>404</error-code>
   <location>/ErrorHandler</location>
</error-page>

<error-page>
   <exception-type>java.lang.Throwable</exception-type >
   <location>/ErrorHandler</location>
</error-page>

现在尝试使用引发任何异常或输入错误 URL 的 servlet,这将触发 Web 容器调用 ErrorHandler servlet 并按编程显示适当的消息。例如,如果您输入错误的 URL,则将显示以下结果 −

The status code : 404

上述代码可能无法在某些 Web 浏览器上运行。因此,请尝试使用 Mozilla 和 Safari,它应该可以运行。

Servlets - Cookies 处理

Cookies 是存储在客户端计算机上的文本文件,用于各种信息跟踪目的。Java Servlets 透明地支持 HTTP cookies。

识别回访用户涉及三个步骤 −

  • 服务器脚本向浏览器发送一组 cookies。例如姓名、年龄或身份证号码等。

  • 浏览器将此信息存储在本地机器上以备将来使用。

  • 当浏览器下次向 Web 服务器发送任何请求时,它会将这些 cookie 信息发送到服务器,服务器使用该信息来识别用户。

本章将教您如何设置或重置 cookie、如何访问它们以及如何删除它们。

Cookie 的剖析

Cookie 通常在 HTTP 标头中设置(尽管 JavaScript 也可以直接在浏览器上设置 cookie)。设置 cookie 的 servlet 可能会发送类似这样的标头 −

HTTP/1.1 200 OK
Date: Fri, 04 Feb 2000 21:03:38 GMT
Server: Apache/1.3.9 (UNIX) PHP/4.0b3
Set-Cookie: name = xyz; expires = Friday, 04-Feb-07 22:03:38 GMT; 
   path = /; domain = tutorialspoint.com
Connection: close
Content-Type: text/html

如您所见,Set-Cookie 标头包含一个名称值对、一个 GMT 日期、一个路径和一个域。名称和值将进行 URL 编码。expires 字段指示浏览器在给定的时间和日期之后"忘记"cookie。

如果浏览器配置为存储 cookie,它将保留此信息直到到期日期。如果用户将浏览器指向与 cookie 的路径和域匹配的任何页面,它将重新将 cookie 发送到服务器。浏览器的标头可能看起来像这样 −

GET / HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.6 (X11; I; Linux 2.2.6-15apmac ppc)
Host: zink.demon.co.uk:1126
Accept: image/gif, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Cookie: name = xyz

然后,servlet 将通过请求方法 request.getCookies() 访问 cookie,该方法返回一个 Cookie 对象数组。

Servlet Cookies 方法

以下是在 servlet 中操作 cookie 时可以使用的有用方法列表。

Sr.No. 方法 &描述
1

public void setDomain(String pattern)

此方法设置 cookie 适用的域,例如 tutorialspoint.com。

2

public String getDomain()

此方法获取 cookie 适用的域,例如 tutorialspoint.com。

3

public void setMaxAge(int expiry)

此方法设置 cookie 的持续时间(单位:分钟)秒)后,cookie 才会过期。如果不设置,cookie 只会在当前会话中有效。

4

public int getMaxAge()

此方法返回 cookie 的最大使用期限,以秒为单位,默认情况下为 -1,表示 cookie 将一直存在,直到浏览器关闭。

5

public String getName()

此方法返回 cookie 的名称。创建后无法更改名称。

6

public void setValue(String newValue)

此方法设置与 cookie 关联的值

7

public String getValue()

此方法获取与 cookie 关联的值。

8

public void setPath(String uri)

此方法设置此 cookie 适用的路径。如果不指定路径,则将返回当前页面同一目录下所有 URL 及其子目录的 cookie。

9

public String getPath()

此方法获取此 cookie 适用的路径。

10

public void setSecure(boolean flag)

此方法设置布尔值,指示 cookie 是否应仅通过加密(即 SSL)连接发送。

11

public void setComment(String purpose)

此方法指定描述 cookie 用途的注释。如果浏览器向用户显示 cookie,则注释很有用。

12

public String getComment()

此方法返回描述此 cookie 用途的注释,如果 cookie 没有注释,则返回 null。

使用 Servlet 设置 Cookie

使用 servlet 设置 cookie 涉及三个步骤 −

(1) 创建 Cookie 对象 − 使用 cookie 名称和 cookie 值调用 Cookie 构造函数,两者都是字符串。

Cookie cookie = new Cookie("key","value");

请记住,名称和值都不应包含空格或以下任何字符 −

[ ] ( ) = , " / ? @ : ;

(2) 设置最大年龄 − 您可以使用 setMaxAge 指定 cookie 的有效时间(以秒为单位)。以下将设置一个 24 小时的 cookie。

cookie.setMaxAge(60 * 60 * 24);

(3) 将 Cookie 发送到 HTTP 响应标头 − 您可以使用 response.addCookie 在 HTTP 响应标头中添加 cookie,如下所示 −

response.addCookie(cookie);

示例

让我们修改我们的表单示例,以设置名字和姓氏的 cookie。

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
// 扩展 HttpServlet 类
public class HelloForm extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 为名字和姓氏创建 cookie。
      Cookie firstName = new Cookie("first_name", request.getParameter("first_name"));
      Cookie lastName = new Cookie("last_name", request.getParameter("last_name"));

      // 将两种 cookie 的有效期都设置为 24 小时后。
      firstName.setMaxAge(60*60*24);
      lastName.setMaxAge(60*60*24);

      // 在响应标头中添加这两个 cookie。
      response.addCookie( firstName );
      response.addCookie( lastName );

      // 设置响应内容类型
      response.setContentType("text/html");
 
      PrintWriter out = response.getWriter();
      String title = "Setting Cookies Example";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
            "<head>
               <title>" + title + "</title>
            </head>" ++
            
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + title + "</h1>" ++
               "<ul>" ++
                  "  <li><b>First Name</b>: "
                  + request.getParameter("first_name") + "" ++
                  "  <li><b>Last Name</b>: "
                  + request.getParameter("last_name") + "" ++
               "</ul>" ++
            "</body>
         </html>"
      );
   }
}

编译上述 servlet HelloForm 并在 web.xml 文件中创建适当的条目,最后尝试以下 HTML 页面调用 servlet。

 
<html>
   <body>
      <form action = "HelloForm" method = "GET">
         First Name: <input type = "text" name = "first_name">
         <br />
         Last Name: <input type = "text" name = "last_name" />
         <input type = "submit" value = "Submit" />
      </form>
   </body>
</html>

将上述 HTML 内容保存在文件 Hello.htm 中,并将其放在 <Tomcat-installationdirectory>/webapps/ROOT 目录中。当您访问 http://localhost:8080/Hello.htm 时,以下是上述表单的实际输出。

First Name:
Last Name:

尝试输入名字和姓氏,然后单击提交按钮。这将在您的屏幕上显示名字和姓氏,同时它将设置两个 cookie firstName 和 lastName,当您下次按下提交按钮时,它们将传递回服务器。

下一节将向您解释如何在 Web 应用程序中访问这些 cookie。

使用 Servlet 读取 Cookie

要读取 cookie,您需要通过调用 HttpServletRequestgetCookies() 方法创建一个 javax.servlet.http.Cookie 对象数组。然后循环遍历数组,并使用 getName() 和 getValue() 方法访问每个 cookie 和相关值。

示例

让我们读取我们在上一个示例中设置的 cookie −

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
// 扩展 HttpServlet 类
public class ReadCookies extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      Cookie cookie = null;
      Cookie[] cookies = null;

      // Get an array of Cookies associated with this domain
      cookies = request.getCookies();

      // 设置响应内容类型
      response.setContentType("text/html");

      PrintWriter out = response.getWriter();
      String title = "Reading Cookies Example";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " +
         "transitional//en\">";
         
      out.println(docType +
         "<html>" ++
         "<head><title>" + title + "</title></head>" ++
         "<body bgcolor = \"#f0f0f0\">" +);

      if( cookies != null ) {
         out.println("<h2> Found Cookies Name and Value</h2>");

         for (int i = 0; i < cookies.length; i++) {
            cookie = cookies[i];
            out.print("Name : " + cookie.getName( ) + ",  ");
            out.print("Value: " + cookie.getValue( ) + " <br/>");
         }
      } else {
         out.println("<h2>No cookies founds</h2>");
      }
      out.println("</body>");
      out.println("</html>");
   }
}

编译上述 servlet ReadCookies 并在 web.xml 文件中创建相应条目。如果您将 first_name cookie 设置为"John",将 last_name cookie 设置为"Player",则运行 http://localhost:8080/ReadCookies 将显示以下结果 −

Found Cookies Name and Value

Name : first_name, Value: John
Name : last_name, Value: Player

使用 Servlet 删除 Cookies

删除 cookies 非常简单。如果您想删除 cookie,则只需执行以下三个步骤即可 −

  • 读取已经存在的 cookie 并将其存储在 Cookie 对象中。

  • 使用 setMaxAge() 方法将 cookie 年龄设置为零以删除现有 cookie

  • 将此 cookie 重新添加到响应标头中。

示例

以下示例将删除名为"first_name"的现有 cookie,当您下次运行 ReadCookies servlet 时,它将返回 first_name 的空值。

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
// 扩展 HttpServlet 类
public class DeleteCookies extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      Cookie cookie = null;
      Cookie[] cookies = null;
         
      // 获取与此域关联的 Cookies 数组
      cookies = request.getCookies();

      // 设置响应内容类型
      response.setContentType("text/html");
 
      PrintWriter out = response.getWriter();
      String title = "Delete Cookies Example";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
         
      out.println(docType +
         "<html>" ++
         "<head><title>" + title + "</title></head>" ++
         "<body bgcolor = \"#f0f0f0\">" +);
         
      if( cookies != null ) {
         out.println("<h2> Cookies Name and Value</h2>");

         for (int i = 0; i < cookies.length; i++) {
            cookie = cookies[i];

            if((cookie.getName( )).compareTo("first_name") == 0 ) {
               cookie.setMaxAge(0);
               response.addCookie(cookie);
               out.print("Deleted cookie : " + cookie.getName( ) + "<br/>");
            }
            out.print("Name : " + cookie.getName( ) + ",  ");
            out.print("Value: " + cookie.getValue( )+" <br/>");
         }
      } else {
         out.println("<h2>No cookies founds</h2>");
      }
      out.println("</body>");
      out.println("</html>");
   }
}

编译上述 servlet DeleteCookies 并在 web.xml 文件中创建相应条目。现在运行 http://localhost:8080/DeleteCookies 将显示以下结果 −

Cookies 名称和值

Deleted cookie : first_name

Name : first_name, Value: John

Name : last_name, Value: Player

现在尝试运行 http://localhost:8080/ReadCookies 并且它将仅显示一个 cookie,如下所示 −

找到 Cookies 名称和值

Name : last_name, Value: Player

您可以在 Internet Explorer 中手动删除 cookie。从"工具"菜单开始,然后选择"Internet 选项"。要删除所有 Cookie,请按"删除 Cookie"。

Servlet - 会话跟踪

HTTP 是一种"无状态"协议,这意味着每次客户端检索网页时,客户端都会打开与 Web 服务器的单独连接,并且服务器不会自动保留任何先前客户端请求的记录。

仍有以下三种方法可以维护 Web 客户端和 Web 服务器之间的会话 −

Cookies

Web 服务器可以为每个 Web 客户端分配一个唯一的会话 ID 作为 cookie,对于来自客户端的后续请求,可以使用收到的 cookie 进行识别。

这可能不是一种有效的方式,因为很多时候浏览器不支持 cookie,所以我不建议使用此过程来维护会话。

隐藏表单字段

Web 服务器可以发送隐藏的 HTML 表单字段以及唯一的会话 ID,如下所示 −

<input type = "hidden" name = "sessionid" value = "12345">

此条目表示,提交表单时,指定的名称和值将自动包含在 GET 或 POST 数据中。每次 Web 浏览器发回请求时,session_id 值都可用于跟踪不同的 Web 浏览器。

这可能是跟踪会话的有效方法,但单击常规 (<A HREF...>) 超文本链接不会导致表单提交,因此隐藏的表单字段也无法支持常规会话跟踪。

URL 重写

您可以在每个 URL 末尾附加一些标识会话的额外数据,服务器可以将该会话标识符与其存储的有关该会话的数据相关联。

例如,对于 http://tutorialspoint.com/file.htm;sessionid = 12345,会话标识符附加为 sessionid = 12345,可在 Web 服务器上访问以识别客户端。

URL 重写是一种更好的维护会话的方法,即使浏览器不支持 cookie 也可以工作。 URL 重写的缺点是,您必须动态生成每个 URL 以分配会话 ID,即使是简单的静态 HTML 页面也是如此。

HttpSession 对象

除了上述三种方式外,servlet 还提供了 HttpSession 接口,该接口提供了一种在多个页面请求或网站访问中识别用户并存储有关该用户的信息的方法。

servlet 容器使用此接口在 HTTP 客户端和 HTTP 服务器之间创建会话。会话在用户的多个连接或页面请求中持续指定的时间段。

您可以通过调用 HttpServletRequest 的公共方法 getSession() 来获取 HttpSession 对象,如下所示 −

HttpSession session = request.getSession();

在向客户端发送任何文档内容之前,您需要调用 request.getSession()。以下是通过 HttpSession 对象可用的重要方法的摘要 −

Sr.No. 方法 &描述
1

public Object getAttribute(String name)

此方法返回此会话中与指定名称绑定的对象,如果名称下没有绑定任何对象,则返回 null。

2

public Enumeration getAttributeNames()

此方法返回一个 String 对象的枚举,其中包含绑定到此会话的所有对象的名称。

3

public long getCreationTime()

此方法返回此会话的创建时间,以自 1970 年 1 月 1 日午夜 GMT 以来的毫秒数来衡量。

4

public String getId()

此方法返回一个字符串,其中包含分配给此会话的唯一标识符。

5

public long getLastAccessedTime()

此方法返回会话的最后访问时间,以自 1970 年 1 月 1 日午夜 GMT 以来的毫秒数格式

6

public int getMaxInactiveInterval()

此方法返回 servlet 容器在客户端访问之间保持会话打开的最大时间间隔(秒)。

7

public void invalidate()

此方法使此会话无效并解除与其绑定的所有对象的绑定。

8

public boolean isNew(

如果客户端尚不知道会话或客户端选择不加入会话,则此方法返回 true。

9

public void removeAttribute(String name)

此方法从此会话中删除与指定名称绑定的对象。

10

public void setAttribute(String name, Object value)

此方法使用名称将对象绑定到此会话指定。

11

public void setMaxInactiveInterval(int interval)

此方法指定客户端请求之间的时间(以秒为单位),在此时间之后 servlet 容器将使此会话无效。

会话跟踪示例

此示例描述如何使用 HttpSession 对象找出会话的创建时间和上次访问时间。如果尚不存在会话,我们会将新会话与请求关联。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
 
// Extend HttpServlet class
public class SessionTrack extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
         
      // 如果尚未创建会话对象,则创建一个。
      HttpSession session = request.getSession(true);
    
      // 获取会话创建时间。
      Date createTime = new Date(session.getCreationTime());
    
      // 获取此网页的最后访问时间。
      Date lastAccessTime = new Date(session.getLastAccessedTime());

      String title = "Welcome Back to my website";
      Integer visitCount = new Integer(0);
      String visitCountKey = new String("visitCount");
      String userIDKey = new String("userID");
      String userID = new String("ABCD");

      // 检查这是否是您的网页上的新来者。
      if (session.isNew()) {
         title = "Welcome to my website";
         session.setAttribute(userIDKey, userID);
      } else {
         visitCount = (Integer)session.getAttribute(visitCountKey);
         visitCount = visitCount + 1;
         userID = (String)session.getAttribute(userIDKey);
      }
      session.setAttribute(visitCountKey,  visitCount);

      // 设置响应内容类型
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " +
         "transitional//en\">";

      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + title + "</h1>" ++
               "<h2 align = \"center\">Session Infomation</h2>" ++
               "<table border = \"1\" align = \"center\">" ++
                  
                  "<tr bgcolor = \"#949494\">" ++
                     "  <th>Session info</th><th>value</th>
                  </tr>" ++
                     
                  "<tr>" ++
                     "  <td>id</td>" ++
                     "  <td>" + session.getId() + "</td>
                  </tr>" ++
                  
                  "<tr>" ++
                     "  <td>Creation Time</td>" ++
                     "  <td>" + createTime + "  </td>
                  </tr>" ++
                  
                  "<tr>" ++
                     "  <td>Time of Last Access</td>" ++
                     "  <td>" + lastAccessTime + "  </td>
                  </tr>" ++
                  
                  "<tr>" ++
                     "  <td>User ID</td>" ++
                     "  <td>" + userID + "  </td>
                  </tr>" ++
                  
                  "<tr>" ++
                     "  <td>Number of visits</td>" ++
                     "  <td>" + visitCount + "</td>
                  </tr>" ++
               "</table>" ++
            "</body>
         </html>"
      );
   }
}

编译上述 servlet SessionTrack 并在 web.xml 文件中创建相应条目。现在运行 http://localhost:8080/SessionTrack 将在首次运行时显示以下结果 −

Welcome to my website

Session Infomation

Session info value
id 0AE3EC93FF44E3C525B4351B77ABB2D5
Creation Time Tue Jun 08 17:26:40 GMT+04:00 2010
Time of Last Access Tue Jun 08 17:26:40 GMT+04:00 2010
User ID ABCD
Number of visits 0

现在尝试第二次运行同一个 servlet,它将显示以下结果。

Welcome Back to my website

Session Infomation

info type value
id 0AE3EC93FF44E3C525B4351B77ABB2D5
Creation Time Tue Jun 08 17:26:40 GMT+04:00 2010
Time of Last Access Tue Jun 08 17:26:40 GMT+04:00 2010
User ID ABCD
Number of visits 1

删除会话数据

处理完用户的会话数据后,您有多种选择 −

  • 删除特定属性 − 您可以调用 public void removeAttribute(String name) 方法来删​​除与特定键关联的值。

  • 删除整个会话 − 您可以调用 public void invalidate() 方法来丢弃整个会话。

  • 设置会话超时 − 您可以调用 public void setMaxInactiveInterval(int interval) 方法来单独设置会话的超时。

  • 注销用户 −对于支持 servlet 2.4 的服务器,您可以调用 logout 将客户端从 Web 服务器注销,并使所有用户的所有会话无效。

  • web.xml 配置 − 如果您使用的是 Tomcat,除了上述方法外,您还可以在 web.xml 文件中按如下方式配置会话超时。

<session-config>
   <session-timeout>15</session-timeout>
</session-config>

超时以分钟表示,并覆盖 Tomcat 中的默认超时(30 分钟)。

servlet 中的 getMaxInactiveInterval() 方法以秒为单位返回该会话的超时时间。因此,如果您的会话在 web.xml 中配置为 15 分钟,则 getMaxInactiveInterval() 将返回 900。

Servlets - 数据库访问

本教程假设您了解 JDBC 应用程序的工作原理。在开始通过 servlet 访问数据库之前,请确保您已正确设置 JDBC 环境以及数据库。

有关如何使用 JDBC 访问数据库及其环境设置的更多详细信息,您可以阅读我们的 JDBC 教程

从基本概念开始,让我们创建一个简单的表并在该表中创建一些记录,如下所示 −

创建表

要在 TEST 数据库中创建 Employees 表,请使用以下步骤 −

步骤 1

打开 命令提示符 并更改为安装目录,如下所示 −

C:\>
C:\>cd Program Files\MySQL\bin
C:\Program Files\MySQL\bin>

步骤 2

按如下方式登录数据库

C:\Program Files\MySQL\bin>mysql -u root -p
Enter password: ********
mysql>

步骤 3

TEST 数据库中创建表 Employee,如下所示 −

mysql> use TEST;
mysql> create table Employees (
   id int not null,
   age int not null,
   first varchar (255),
   last varchar (255)
);
Query OK, 0 rows affected (0.08 sec)
mysql>

创建数据记录

最后,在员工表中创建几条记录,如下所示−

mysql> INSERT INTO Employees VALUES (100, 18, 'Zara', 'Ali');
Query OK, 1 row affected (0.05 sec)
 
mysql> INSERT INTO Employees VALUES (101, 25, 'Mahnaz', 'Fatma');
Query OK, 1 row affected (0.00 sec)
 
mysql> INSERT INTO Employees VALUES (102, 30, 'Zaid', 'Khan');
Query OK, 1 row affected (0.00 sec)
 
mysql> INSERT INTO Employees VALUES (103, 28, 'Sumit', 'Mittal');
Query OK, 1 row affected (0.00 sec)
 
mysql>

访问数据库

下面是一个示例,展示了如何使用 Servlet 访问 TEST 数据库。

// 加载所需的库
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
 
public class DatabaseAccess extends HttpServlet{

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   
      // JDBC 驱动程序名称和数据库 URL
      static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  
      static final String DB_URL="jdbc:mysql://localhost/TEST";

      //  数据库凭据
      static final String USER = "root";
      static final String PASS = "password";

      // 设置响应内容类型
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      String title = "Database Result";
      
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
         "<head><title>" + title + "</title></head>" ++
         "<body bgcolor = \"#f0f0f0\">" ++
         "<h1 align = \"center\">" + title + "</h1>
");
      try {
         // 注册 JDBC 驱动程序
         Class.forName("com.mysql.jdbc.Driver");

         // 打开连接
         Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);

         // 执行 SQL 查询
         Statement stmt = conn.createStatement();
         String sql;
         sql = "SELECT id, first, last, age FROM Employees";
         ResultSet rs = stmt.executeQuery(sql);

         // 从结果集提取数据
         while(rs.next()){
            //按列名检索
            int id  = rs.getInt("id");
            int age = rs.getInt("age");
            String first = rs.getString("first");
            String last = rs.getString("last");

            //显示值
            out.println("ID: " + id + "<br>");
            out.println(", Age: " + age + "<br>");
            out.println(", First: " + first + "<br>");
            out.println(", Last: " + last + "<br>");
         }
         out.println("</body></html>");

         // Clean-up environment
         rs.close();
         stmt.close();
         conn.close();
      } catch(SQLException se) {
         //处理 JDBC 错误
         se.printStackTrace();
      } catch(Exception e) {
         //处理 Class.forName 的错误
         e.printStackTrace();
      } finally {
         //finally 块用于关闭资源
         try {
            if(stmt!=null)
               stmt.close();
         } catch(SQLException se2) {
         } // nothing we can do
         try {
            if(conn!=null)
            conn.close();
         } catch(SQLException se) {
            se.printStackTrace();
         } //end finally try
      } //end try
   }
} 

现在让我们编译上述 servlet 并在 web.xml 中创建以下条目

....
<servlet>
   <servlet-name>DatabaseAccess</servlet-name>
   <servlet-class>DatabaseAccess</servlet-class>
</servlet>
 
<servlet-mapping>
   <servlet-name>DatabaseAccess</servlet-name>
   <url-pattern>/DatabaseAccess</url-pattern>
</servlet-mapping>
....

现在使用 URL http://localhost:8080/DatabaseAccess 调用此 servlet,它将显示以下响应 −

Database Result

ID: 100, Age: 18, First: Zara, Last: Ali ID: 101, Age: 25, First: Mahnaz, Last: Fatma ID: 102, Age: 30, First: Zaid, Last: Khan ID: 103, Age: 28, First: Sumit, Last: Mittal

Servlet - 文件上传

Servlet 可与 HTML 表单标记一起使用,以允许用户将文件上传到服务器。上传的文件可以是文本文件、图像文件或任何文档。

创建文件上传表单

以下 HTM 代码创建上传器表单。以下是需要注意的重要点 −

  • 表单 method 属性应设置为 POST 方法,不能使用 GET 方法

  • 表单 enctype 属性应设置为 multipart/form-data

  • 表单 action 属性应设置为 servlet 文件,该文件将在后端服务器上处理文件上传。以下示例使用 UploadServlet servlet 上传文件。

  • 要上传单个文件,您应使用带有属性 type="file" 的单个 <input .../> 标签。要允许上传多个文件,请包含多个具有不同 name 属性值的 input 标签。浏览器会将浏览按钮与每个文件关联。

 
<html>
   <head>
      <title>File Uploading Form</title>
   </head>
   
   <body>
      <h3>File Upload:</h3>
      Select a file to upload: <br />
      <form action = "UploadServlet" method = "post" enctype = "multipart/form-data">
         <input type = "file" name = "file" size = "50" />
         <br />
         <input type = "submit" value = "Upload File" />
      </form>
   </body>
</html>

这将显示以下结果,允许从本地 PC 选择一个文件,当用户单击"上传文件"时,表单将与所选文件一起提交 −

文件上传:
选择要上传的文件:


注意:这只是虚拟表单,不起作用。

编写后端 Servlet

以下是 servlet UploadServlet,它将负责接受上传的文件并将其存储在目录 <Tomcat-installation-directory>/webapps/data 中。还可以使用外部配置(例如 web.xml 中的 context-param 元素)添加此目录名称,如下所示 −

 
<web-app>
   ....
   <context-param> 
      <description>Location to store uploaded file</description> 
      <param-name>file-upload</param-name> 
      <param-value>
         c:\apache-tomcat-5.5.29\webapps\data\
      </param-value> 
   </context-param>
   ....
</web-app>

以下是 UploadServlet 的源代码,它可以一次处理多个文件上传。在继续之前,请确保以下 −

  • 以下示例依赖于 FileUpload,因此请确保您的类路径中具有最新版本的 commons-fileupload.x.x.jar 文件。您可以从 https://commons.apache.org/fileupload/ 下载它。

  • FileUpload 依赖于 Commons IO,因此请确保您的类路径中具有最新版本的 commons-io-x.x.jar 文件。您可以从 https://commons.apache.org/io/ 下载。

  • 在测试以下示例时,您应该上传一个小于 maxFileSize 的文件,否则文件将无法上传。

  • 确保您已提前创建目录 c:emp 和 c:\apache-tomcat8.0.28\webapps\data。

// 导入所需的 Java 库
import java.io.*;
import java.util.*;
 
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.output.*;

public class UploadServlet extends HttpServlet {
   
   private boolean isMultipart;
   private String filePath;
   private int maxFileSize = 50 * 1024;
   private int maxMemSize = 4 * 1024;
   private File file ;

   public void init( ){
      // 获取存储文件的位置。
      filePath = getServletContext().getInitParameter("file-upload"); 
   }
   
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, java.io.IOException {
   
      // 检查我们是否有文件上传请求
      isMultipart = ServletFileUpload.isMultipartContent(request);
      response.setContentType("text/html");
      java.io.PrintWriter out = response.getWriter( );
   
      if( !isMultipart ) {
         out.println("<html>");
         out.println("<head>");
         out.println("<title>Servlet upload</title>");  
         out.println("</head>");
         out.println("<body>");
         out.println("<p>No file uploaded</p>"); 
         out.println("</body>");
         out.println("</html>");
         return;
      }
  
      DiskFileItemFactory factory = new DiskFileItemFactory();
   
      // 存储在内存中的最大大小
      factory.setSizeThreshold(maxMemSize);
   
      // 保存大于 maxMemSize 的数据的位置。
      factory.setRepository(new File("c:\temp"));

      // 创建新的文件上传处理程序
      ServletFileUpload upload = new ServletFileUpload(factory);
   
      // 可上传的最大文件大小。
      upload.setSizeMax( maxFileSize );

      try { 
         // 解析请求以获取文件项。
         List fileItems = upload.parseRequest(request);
	
         // 处理上传的文件项
         Iterator i = fileItems.iterator();

         out.println("<html>");
         out.println("<head>");
         out.println("<title>Servlet upload</title>");  
         out.println("</head>");
         out.println("<body>");
   
         while ( i.hasNext () ) {
            FileItem fi = (FileItem)i.next();
            if ( !fi.isFormField () ) {
               // 获取上传文件参数
               String fieldName = fi.getFieldName();
               String fileName = fi.getName();
               String contentType = fi.getContentType();
               boolean isInMemory = fi.isInMemory();
               long sizeInBytes = fi.getSize();
            
               // 写入文件
               if( fileName.lastIndexOf("\") >= 0 ) {
                  file = new File( filePath + fileName.substring( fileName.lastIndexOf("\"))) ;
               } else {
                  file = new File( filePath + fileName.substring(fileName.lastIndexOf("\")+1)) ;
               }
               fi.write( file ) ;
               out.println("Uploaded Filename: " + fileName + "<br>");
            }
         }
         out.println("</body>");
         out.println("</html>");
         } catch(Exception ex) {
            System.out.println(ex);
         }
      }
      
      public void doGet(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, java.io.IOException {

         throw new ServletException("GET method used with " +
            getClass( ).getName( )+": POST method required.");
      }
   }
}

编译并运行 Servlet

编译上述 servlet UploadServlet 并在 web.xml 文件中创建所需的条目,如下所示。

 
<servlet>
   <servlet-name>UploadServlet</servlet-name>
   <servlet-class>UploadServlet</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>UploadServlet</servlet-name>
   <url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>

现在尝试使用上面创建的 HTML 表单上传文件。当您尝试 http://localhost:8080/UploadFile.htm 时,它将显示以下结果,这将帮助您从本地机器上传任何文件。

文件上传:

选择要上传的文件:


如果您的 servlet 脚本运行正常,您的文件应该上传到 c:\apache-tomcat8.0.28\webapps\data\ 目录中。

Servlet - 处理日期

使用 Servlet 的最重要的优势之一是您可以使用核心 Java 中可用的大多数方法。本教程将带您了解 Java 提供的 Date 类,该类位于 java.util 包中,该类封装了当前日期和时间。

Date 类支持两个构造函数。第一个构造函数使用当前日期和时间初始化对象。

Date( )

以下构造函数接受一个参数,该参数等于自 1970 年 1 月 1 日午夜以来经过的毫秒数

Date(long millisec)

一旦您有可用的 Date 对象,您就可以调用以下任何支持方法来处理日期 −

Sr.No. 方法 &描述
1

boolean after(Date date)

如果调用 Date 对象包含的日期晚于 date 指定的日期,则返回 true,否则返回 false。

2

boolean before(Date date)

如果调用 Date 对象包含的日期早于 date 指定的日期,则返回 true,否则返回 false。

3

Object clone( )

复制调用 Date 对象。

4

int compareTo(Date date)

将调用对象的值与 date 的值进行比较。如果值相等,则返回 0。如果调用对象早于 date,则返回负值。如果调用对象晚于 date,则返回正值。

5

int compareTo(Object obj)

如果 obj 属于 Date 类,则操作与 compareTo(Date) 相同。否则,它会抛出 ClassCastException。

6

boolean equals(Object date)

如果调用的 Date 对象包含与 date 指定的时间和日期相同的时间和日期,则返回 true,否则返回 false。

7

long getTime( )

返回自 1970 年 1 月 1 日以来经过的毫秒数。

8

int hashCode( )

返回调用对象的哈希码。

9

void setTime(long time)

设置由 time 指定的时间和日期,该时间表示从 1970 年 1 月 1 日午夜开始经过的时间(以毫秒为单位)。

10

String toString( )

将调用的 Date 对象转换为字符串并返回结果。

获取当前日期和时间

在 Java Servlet 中获取当前日期和时间非常容易。您可以使用带有 toString() 方法的简单 Date 对象打印当前日期和时间,如下所示 −

// 导入所需的 Java 库
import java.io.*;
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.*;
 
// 扩展 HttpServlet 类
public class CurrentDate extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");
 
      PrintWriter out = response.getWriter();
      String title = "Display Current Date & Time";
      Date date = new Date();
      String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + title + "</h1>" ++
               "<h2 align = \"center\">" + date.toString() + "</h2>" ++
            "</body>
         </html>"
      );
   }
}

现在让我们编译上述 servlet 并在 web.xml 中创建适当的条目,然后使用 URL http://localhost:8080/CurrentDate 调用此 servlet。这将产生以下结果 −

Display Current Date & Time

Mon Jun 21 21:46:49 GMT+04:00 2010

尝试刷新 URL http://localhost:8080/CurrentDate,每次刷新时您都会发现秒数的差异。

日期比较

正如我上面提到的,您可以在 Servlet 中使用所有可用的 Java 方法。如果您需要比较两个日期,可以使用以下方法 −

  • 您可以使用 getTime() 获取两个对象自 1970 年 1 月 1 日午夜以来经过的毫秒数,然后比较这两个值。

  • 您可以使用方法 before()、after() 和 equals()。例如,由于月份 12 号在 18 号之前,因此 new Date(99, 2, 12).before(new Date (99, 2, 18)) 返回 true。

  • 您可以使用 compareTo( ) 方法,该方法由 Comparable 接口定义并由 Date 实现。

使用 SimpleDateFormat 进行日期格式化

SimpleDateFormat 是一个具体的类,用于以区域敏感的方式格式化和解析日期。SimpleDateFormat 允许您从选择任何用户定义的日期时间格式模式开始。

让我们将上面的例子修改如下 −

// 导入所需的 Java 库
import java.io.*;
import java.text.*;
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.*;
 
// 扩展 HttpServlet 类
public class CurrentDate extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");
 
      PrintWriter out = response.getWriter();
      String title = "Display Current Date & Time";
      Date dNow = new Date( );
      SimpleDateFormat ft = new SimpleDateFormat ("E yyyy.MM.dd 'at' hh:mm:ss a zzz");
      String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + title + "</h1>" ++
               "<h2 align = \"center\">" + ft.format(dNow) + "</h2>" ++
            "</body>
         </html>"
      );
   }
}

再次编译上述 servlet,然后使用 URL http://localhost:8080/CurrentDate 调用此 servlet。这将产生以下结果 −

显示当前日期和时间

2010.06.21 星期一,晚上 10:06:44 GMT+04:00

简单的 DateFormat 格式代码

要指定时间格式,请使用时间模式字符串。在此模式中,所有 ASCII 字母都保留为模式字母,其定义为以下 −

字符 描述 示例
G 时代指示符 AD
y 四位数年份 2001
M 一年中的月份 七月或 07
d 月份中的日期 10
h 上午/下午的小时(1~12) 12
H 一天中的小时(0~23) 22
m 一小时中的分钟 30
s 一分钟中的秒 55
S 毫秒 234
E 一天中的周 星期二
D 一年中的天数 360
F 一个月中的星期几 2(七月的第二个星期三)
w 一年中的周数 40
W 一个月中的周数 1
a 上午/下午标记 PM
k 一天中的小时 (1~24) 24
K 上午/下午中的小时(0~11) 10
z 时区 东部标准时间
' 文本转义 分隔符
" 单引号 `

有关可用于操作日期的常量方法的完整列表,可以参考标准 Java 文档。

Servlets - 页面重定向

页面重定向是一种将客户端发送到请求位置以外的新位置的技术。页面重定向通常用于文档移动到新位置或可能由于负载平衡而发生的情况。

将请求重定向到另一个页面的最简单方法是使用响应对象的方法 sendRedirect()。以下是此方法的签名 −

public void HttpServletResponse.sendRedirect(String location)
throws IOException

此方法将响应连同状态代码和新页面位置一起发送回浏览器。您还可以结合使用 setStatus() 和 setHeader() 方法来实现相同的 −

....
String site = "http://www.newpage.com" ;
response.setStatus(response.SC_MOVED_TEMPORARILY);
r​​esponse.setHeader("Location", site);
....

示例

此示例显示 servlet 如何执行页面重定向到另一个位置 −

import java.io.*;
import java.sql.Date;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class PageRedirect extends HttpServlet {
    
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // 设置响应内容类型
      response.setContentType("text/html");

      // 要重定向的新位置
      String site = new String("http://www.photofuntoos.com");

      response.setStatus(response.SC_MOVED_TEMPORARILY);
      response.setHeader("Location", site);    
   }
} 

现在让我们编译上述 servlet 并在 web.xml 中创建以下条目

....
<servlet>
   <servlet-name>PageRedirect</servlet-name>
   <servlet-class>PageRedirect</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>PageRedirect</servlet-name>
   <url-pattern>/PageRedirect</url-pattern>
</servlet-mapping>
....

现在使用 URL http://localhost:8080/PageRedirect 调用此 servlet。这会将您重定向到 URL http://www.photofuntoos.com。

Servlet - 点击计数器

网页点击计数器

很多时候,您可能想知道网站某个页面的点击总数。使用 servlet 来计算这些点击非常简单,因为 servlet 的生命周期由其运行的容器控制。

下面是基于 Servlet 生命周期 − 实现简单页面点击计数器的步骤

  • 在 init() 方法中初始化全局变量。

  • 每次调用 doGet() 或 doPost() 方法时增加全局变量。

  • 如果需要,您可以使用数据库表来存储 destroy() 方法中的全局变量的值。下次初始化 servlet 时,可以在 init() 方法中读取此值。此步骤是可选的。

  • 如果您只想计算会话中唯一的页面点击次数,则可以使用 isNew() 方法来检查同一页面是否已在该会话中被点击。此步骤是可选的。

  • 您可以显示全局计数器的值以显示您网站上的总点击次数。此步骤也是可选的。

这里我假设 Web 容器不会重新启动。如果重新启动或 servlet 被销毁,点击计数器将被重置。

示例

此示例展示了如何实现一个简单的页面点击计数器 −

import java.io.*;
import java.sql.Date;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class PageHitCounter extends HttpServlet {

   private int hitCount; 

   public void init() { 
      // 重置点击计数器。
      hitCount = 0;
   } 

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // 设置响应内容类型
      response.setContentType("text/html");

      // 每当 servlet 被点击时,此方法就会执行
      // 增加 hitCount
      hitCount++; 
      PrintWriter out = response.getWriter();
      String title = "Total Number of Hits";
      String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + title + "</h1>" ++
               "<h2 align = \"center\">" + hitCount + "</h2>" ++
            "</body>
         </html>"
      );
   }
   
   public void destroy() { 
    // 这是可选步骤,但如果您愿意,您可以
    // 在您的数据库中写入 hitCount 值。
   } 
} 

现在让我们编译上述 servlet 并在 web.xml 中创建以下条目

<servlet>
   <servlet-name>PageHitCounter</servlet-name>
   <servlet-class>PageHitCounter</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>PageHitCounter</servlet-name>
   <url-pattern>/PageHitCounter</url-pattern>
</servlet-mapping>
....

现在使用 URL http://localhost:8080/PageHitCounter 调用此 servlet。每次刷新此页面时,计数器都会增加一,并显示以下结果 −

总点击次数

6

网站点击计数器:

很多时候,您会想知道整个网站的总点击次数。在 Servlet 中这也非常简单,我们可以使用过滤器来实现。

以下是基于过滤器生命周期实现简单网站点击计数器的步骤 −

  • 在过滤器的 init() 方法中初始化全局变量。

  • 每次调用 doFilter 方法时增加全局变量。

  • 如果需要,您可以使用数据库表在过滤器的 destroy() 方法中存储全局变量的值。下次初始化过滤器时,可以在 init() 方法中读取此值。此步骤是可选的。

这里我假设 Web 容器不会重新启动。如果重新启动或 servlet 被销毁,点击计数器将被重置。

示例

此示例展示了如何实现简单的网站点击计数器 −

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

public class SiteHitCounter implements Filter {

   private int hitCount; 

   public void  init(FilterConfig config) throws ServletException {
    // 重置命中计数器。
    hitCount = 0;
   }

   public void  doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
      throws java.io.IOException, ServletException {
    // 将计数器增加一
    hitCount++;
    
    // 打印计数器。
    System.out.println("网站访问次数:"+ hitCount );
    
    // 将请求传递回过滤器链
    chain.doFilter(request,response);
   }
   
   public void destroy() { 
    // 这是可选步骤,但如果您愿意,您可以
    // 在您的数据库中写入 hitCount 值。
   } 
} 

现在让我们编译上述 servlet 并在 web.xml 中创建以下条目

....
<filter>
   <filter-name>SiteHitCounter</filter-name>
   <filter-class>SiteHitCounter</filter-class>
</filter>

<filter-mapping>
   <filter-name>SiteHitCounter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>
....

现在调用任何 URL,例如 URL http://localhost:8080/。每当任何页面获得点击时,计数器都会增加一,并在日志中显示以下消息 −

Site visits count : 1
Site visits count : 2
Site visits count : 3
Site visits count : 4
Site visits count : 5
..................

Servlet - 自动页面刷新

假设有一个网页正在显示实时比赛比分、股票市场状态或货币兑换率。对于所有此类页面,您需要使用浏览器中的刷新或重新加载按钮定期刷新网页。

Java Servlet 通过提供一种机制使这项工作变得简单,您可以让网页在给定间隔后自动刷新。

刷新网页的最简单方法是使用响应对象的方法 setIntHeader()。以下是该方法的签名 −

public void setIntHeader(String header, int headerValue)

该方法将标头"Refresh"连同表示时间间隔(以秒为单位)的整数值一起发送回浏览器。

自动页面刷新示例

此示例显示了 servlet 如何使用 setIntHeader() 方法设置 Refresh 标头来执行自动页面刷新。

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
 
// 扩展 HttpServlet 类
public class Refresh extends HttpServlet {
 
   // 处理 GET 方法请求的方法。
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置刷新、自动加载时间为 5 秒
      response.setIntHeader("Refresh", 5);
 
      // 设置响应内容类型
      response.setContentType("text/html");
 
      // 获取当前时间
      Calendar calendar = new GregorianCalendar();
      String am_pm;
      int hour = calendar.get(Calendar.HOUR);
      int minute = calendar.get(Calendar.MINUTE);
      int second = calendar.get(Calendar.SECOND);
      
      if(calendar.get(Calendar.AM_PM) == 0)
        am_pm = "AM";
      else
        am_pm = "PM";
 
      String CT = hour+":"+ minute +":"+ second +" "+ am_pm;
    
      PrintWriter out = response.getWriter();
      String title = "Auto Page Refresh using Servlet";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
         "<head><title>" + title + "</title></head>
"+
         "<body bgcolor = \"#f0f0f0\">" ++
         "<h1 align = \"center\">" + title + "</h1>" ++
         "<p>Current Time is: " + CT + "</p>
"
      );
   }
   
   // 处理 POST 方法请求的方法。
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      doGet(request, response);
   }
}

现在让我们编译上述 servlet 并在 web.xml 中创建以下条目

....
 <servlet>
     <servlet-name>Refresh</servlet-name>
     <servlet-class>Refresh</servlet-class>
 </servlet>
 
 <servlet-mapping>
     <servlet-name>Refresh</servlet-name>
     <url-pattern>/Refresh</url-pattern>
 </servlet-mapping>
....

现在使用 URL http://localhost:8080/Refresh 调用此 servlet,它将每 5 秒显示一次当前系统时间,如下所示。只需运行 servlet 并等待查看结果 −

Auto Page Refresh using Servlet

Current Time is: 9:44:50 PM

Servlet - 发送电子邮件

使用 Servlet 发送电子邮件非常简单,但首先您应该在机器上安装 JavaMail APIJava Activation Framework (JAF)

下载并解压缩这些文件,在新创建的顶级目录中,您将找到两个应用程序的大量 jar 文件。您需要在 CLASSPATH 中添加 mail.jaractivation.jar 文件。

发送一封简单的电子邮件

下面是一个从您的机器发送一封简单电子邮件的示例。这里假设您的localhost已连接到互联网并且足以发送电子邮件。同时确保 Java Email API 包和 JAF 包中的所有 jar 文件在 CLASSPATH 中可用。

// File Name SendEmail.java
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
 
public class SendEmail extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    
    // 需要提及收件人的电子邮件 ID。
    String to = "abcd@gmail.com";
    
    // 需要提及发件人的电子邮件 ID
    String from = "web@gmail.com";
    
    // 假设您从本地主机发送电子邮件
    String host = "localhost";
    
    // 获取系统属性
    Properties properties = System.getProperties();
    
    // 设置邮件服务器
    properties.setProperty("mail.smtp.host", host);
    
    // 获取默认 Session 对象。
    Session session = Session.getDefaultInstance(properties);
    
    // 设置响应内容类型
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    
    try {
        // 创建默认的 MimeMessage 对象。
        MimeMessage message = new MimeMessage(session);
        
        // 设置发件人:标头的标头字段。
        message.setFrom(new InternetAddress(from));
        
        // 设置收件人:标头的标头字段。
        message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
        
        // 设置主题:标头字段
        message.setSubject("This is the Subject Line!");
        
        // 现在设置实际消息
        message.setText("This is actual message");
        
        // 发送消息
         Transport.send(message);
         String title = "Send Email";
         String res = "Sent message successfully....";
         String docType =
            "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">
";
         
         out.println(docType +
            "<html>
" +
               "<head><title>" + title + "</title></head>
" +
               "<body bgcolor = \"#f0f0f0\">
" +
                  "<h1 align = \"center\">" + title + "</h1>
" +
                  "<p align = \"center\">" + res + "</p>
" +
               "</body>
            </html>"
         );
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
} 

现在让我们编译上述 servlet 并在 web.xml 中创建以下条目

....
 <servlet>
   <servlet-name>SendEmail</servlet-name>
   <servlet-class>SendEmail</servlet-class>
</servlet>
 
<servlet-mapping>
   <servlet-name>SendEmail</servlet-name>
   <url-pattern>/SendEmail</url-pattern>
</servlet-mapping>
....

现在使用 URL http://localhost:8080/SendEmail 调用此 servlet,它将向给定的电子邮件 ID abcd@gmail.com 发送电子邮件并显示以下响应 −

发送电子邮件

已成功发送消息....

如果您想向多个收件人发送电子邮件,则可以使用以下方法指定多个电子邮件 ID −

void addRecipients(Message.RecipientType type, Address[] address)
throws MessagingException

以下是参数的描述 −

  • type − 这将设置为 TO、CC 或 BCC。这里 CC 代表抄送,BCC 代表黑名单抄送。示例 Message.RecipientType.TO

  • addresses − 这是电子邮件 ID 数组。您需要在指定电子邮件 ID 时使用 InternetAddress() 方法。

发送 HTML 电子邮件

以下是从您的机器发送 HTML 电子邮件的示例。这里假设您的 localhost 已连接到互联网并且足以发送电子邮件。同时,确保 Java Email API 包和 JAF 包中的所有 jar 文件在 CLASSPATH 中可用。

此示例与上一个示例非常相似,只是这里我们使用 setContent() 方法来设置内容,其第二个参数为"text/html",以指定 HTML 内容包含在消息中。

使用此示例,您可以发送任意大小的 HTML 内容。

// File Name SendEmail.java
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
 
public class SendEmail extends HttpServlet {
    
   public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    
    // 需要提及收件人的电子邮件 ID。
    String to = "abcd@gmail.com";
    
    // 需要提及发件人的电子邮件 ID
    String from = "web@gmail.com";
    
    // 假设您从本地主机发送电子邮件
    String host = "localhost";
    
    // 获取系统属性
    Properties properties = System.getProperties();
    
    // 设置邮件服务器
    properties.setProperty("mail.smtp.host", host);
    
    // 获取默认 Session 对象。
    Session session = Session.getDefaultInstance(properties);
    
    // 设置响应内容类型
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    
    try {
         
        // 创建默认的 MimeMessage 对象。
        MimeMessage message = new MimeMessage(session);
        
        // 设置发件人:标头的标头字段。
        message.setFrom(new InternetAddress(from));
        
        // 设置收件人:标头的标头字段。
        message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
        // 设置主题:标头字段
        message.setSubject("This is the Subject Line!");
        
        // 发送实际的 HTML 消息,大小随意
        message.setContent("<h1>This is actual message</h1>", "text/html" );
        
        // 发送消息
         Transport.send(message);
         String title = "Send Email";
         String res = "Sent message successfully....";
         String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">
";
         
         out.println(docType +
            "<html>
" +
               "<head><title>" + title + "</title></head>
" +
               "<body bgcolor = \"#f0f0f0\">
" +
                  "<h1 align = \"center\">" + title + "</h1>
" +
                  "<p align = \"center\">" + res + "</p>
" +
               "</body>
            </html>"
         );
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
} 

编译并运行上述 servlet,以向给定的电子邮件 ID 发送 HTML 消息。

在电子邮件中发送附件

以下是从您的机器发送带有附件的电子邮件的示例。这里假设您的 localhost 已连接到互联网并且足以发送电子邮件。

// File Name SendEmail.java
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
 
public class SendEmail extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    
    // 需要提及收件人的电子邮件 ID。
    String to = "abcd@gmail.com";
    
    // 需要提及发件人的电子邮件 ID
    String from = "web@gmail.com";
    
    // 假设您从本地主机发送电子邮件
    String host = "localhost";
    
    // 获取系统属性
    Properties properties = System.getProperties();
    
    // 设置邮件服务器
    properties.setProperty("mail.smtp.host", host);
    
    // 获取默认 Session 对象。
    Session session = Session.getDefaultInstance(properties);
    
    // 设置响应内容类型
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    
    try {
        // 创建默认的 MimeMessage 对象。
        MimeMessage message = new MimeMessage(session);
        
        // 设置发件人:标头的标头字段。
        message.setFrom(new InternetAddress(from));
        
        // 设置收件人:标头的标头字段。
        message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
        
        // 设置主题:标头字段
        message.setSubject("This is the Subject Line!");
        
        // 创建消息部分
        BodyPart messageBodyPart = new MimeBodyPart();
        
        // 填写消息
        messageBodyPart.setText("This is message body");
        
        // 创建多部分消息
        Multipart multipart = new MimeMultipart();
        
        // 设置文本消息部分
        multipart.addBodyPart(messageBodyPart);
        
        // 第二部分是附件
        messageBodyPart = new MimeBodyPart();
        String filename = "file.txt";
        DataSource source = new FileDataSource(filename);
        messageBodyPart.setDataHandler(new DataHandler(source));
        messageBodyPart.setFileName(filename);
        multipart.addBodyPart(messageBodyPart);
        
        // 发送完整的消息部分
        message.setContent(multipart );
        
        // 发送消息
         Transport.send(message);
         String title = "Send Email";
         String res = "Sent message successfully....";
         String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">
";
         
         out.println(docType +
            "<html>
" +
               "<head><title>" + title + "</title></head>
" +
               "<body bgcolor = \"#f0f0f0\">
" +
                  "<h1 align = \"center\">" + title + "</h1>
" +
                  "<p align = \"center\">" + res + "</p>
" +
               "</body>
            </html>"
         );
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
} 

编译并运行上述 servlet,以将文件作为附件连同消息一起发送到给定的电子邮件 ID。

用户身份验证部分

如果需要向电子邮件服务器提供用户 ID 和密码以进行身份​​验证,则可以按如下方式设置这些属性 −

props.setProperty("mail.user", "myuser");
props.setProperty("mail.password", "mypwd");

其余的电子邮件发送机制将保留如上所述。

Servlet - 打包

涉及 WEB-INF 子目录的 Web 应用程序结构是所有 Java Web 应用程序的标准结构,由 servlet API 规范指定。给定一个顶级目录名称 myapp。此目录结构如下所示 −

/myapp
   /images
   /WEB-INF
      /classes
      /lib

WEB-INF 子目录包含应用程序的部署描述符,名为 web.xml。所有 HTML 文件都应保存在顶层目录 myapp 中。对于管理员用户,您将发现 ROOT 目录是父目录。

在包中创建 Servlet

WEB-INF/classes 目录包含所有 servlet 类和其他类文件,其结构与其包名称相匹配。例如,如果您有一个完全限定的类名 com.myorg.MyServlet,那么这个 servlet 类必须位于以下目录中 −

/myapp/WEB-INF/classes/com/myorg/MyServlet.class

以下是创建包名为 com.myorg 的 MyServlet 类的示例>

// 命名您的包
package com.myorg;

// 导入所需的 java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
public class MyServlet extends HttpServlet {
 
   private String message;
 
   public void init() throws ServletException {
      // 进行必要的初始化
      message = "Hello World";
   }
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");
 
      // 实际逻辑在这里。
      PrintWriter out = response.getWriter();
      out.println("<h1>" + message + "</h1>");
   }

   public void destroy() {
      // do nothing.
   }
}

在包中编译 Servlet

编译包中的类没有什么不同。最简单的方法是将您的 java 文件保存在完全限定路径中,如上所述,类将保存在 com.myorg 中。您还需要在 CLASSPATH 中添加此目录。

假设您的环境已正确设置,请进入 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 目录并按如下方式编译 MyServlet.java

$ javac MyServlet.java

如果 servlet 依赖于任何其他库,您还必须将这些 JAR 文件包含在您的 CLASSPATH 中。我只包含了 servlet-api.jar JAR 文件,因为我在 Hello World 程序中没有使用任何其他库。

此命令行使用 Sun Microsystems Java 软件开发工具包 (JDK) 附带的内置 javac 编译器。要使此命令正常工作,您必须在 PATH 环境变量中包含您正在使用的 Java SDK 的位置。

如果一切顺利,上述编译将在同一目录中生成 MyServlet.class 文件。下一节将解释如何在生产中部署已编译的 servlet。

打包的 Servlet 部署

默认情况下,servlet 应用程序位于路径 <Tomcat-installationdirectory>/webapps/ROOT,类文件将位于 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes。

如果您有一个完全限定的类名 com.myorg.MyServlet,那么这个 servlet 类必须位于 WEB-INF/classes/com/myorg/MyServlet.class,并且您需要在位于 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/

web.xml 文件中创建以下条目
<servlet>
   <servlet-name>MyServlet</servlet-name>
   <servlet-class>com.myorg.MyServlet</servlet-class>
</servlet>
 
<servlet-mapping>
   <servlet-name>MyServlet</servlet-name>
   <url-pattern>/MyServlet</url-pattern>
</servlet-mapping>

上述条目将在 web.xml 文件中可用的 <web-app>...</web-app> 标签内创建。此表中可能已存在各种条目,但没关系。

您几乎已完成,现在让我们使用 <Tomcat-installationdirectory>\bin\startup.bat(在 Windows 上)或 <Tomcat-installationdirectory>/bin/startup.sh(在 Linux/Solaris 等上)启动 tomcat 服务器,最后在浏览器的地址框中输入 http://localhost:8080/MyServlet。如果一切顺利,您将获得以下结果−

Hello World

Servlet - 调试

测试/调试 servlet 总是很困难。Servlet 往往涉及大量客户端/服务器交互,因此很容易出错,但很难重现。

以下是一些可能有助于您进行调试的提示和建议。

System.out.println()

System.out.println() 很容易用作标记来测试某段代码是否正在执行。我们也可以打印出变量值。此外 −

  • 由于 System 对象是核心 Java 对象的一部分,因此可以在任何地方使用它,而无需安装任何额外的类。这包括 Servlet、JSP、RMI、EJB、普通 Bean 和类以及独立应用程序。

  • 在断点处停止技术会停止正常执行,因此需要更多时间。而写入 System.out 不会对应用程序的正常执行流程造成太大干扰,这在时间至关重要时非常有用。

以下是使用 System.out.println() 的语法 −

System.out.println("Debugging message");

上述语法生成的所有消息都将记录在 Web 服务器日志文件中。

消息记录

使用适当的记录方法记录所有调试、警告和错误消息始终是一个好主意,使用标准记录方法。我使用 log4J 来记录所有消息。

Servlet API 还提供了一种使用 log() 方法输出信息的简单方法,如下所示 −

// 导入所需的 Java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ContextLog extends HttpServlet {
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, java.io.IOException {
    
      String par = request.getParameter("par1");
      
      //调用两个ServletContext.log方法
      ServletContext context = getServletContext( );

      if (par == null || par.equals(""))
         //带有 Throwable 参数的日志版本
         context.log("No message received:", new IllegalStateException("Missing parameter"));
      else
         context.log("Here is the visitor's message: " + par);
      
      response.setContentType("text/html");
      java.io.PrintWriter out = response.getWriter( );
      String title = "Context Log";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + title + "</h1>" ++
               "<h2 align = \"center\">Messages sent</h2>" ++
            "</body>
         </html>"
      );
   } //doGet
}

ServletContext 将其文本消息记录到 servlet 容器的日志文件中。对于 Tomcat,这些日志位于 <Tomcat-installation-directory>/logs 中。

日志文件确实会指示新出现的错误或问题发生的频率。因此,最好在异常的 catch 子句中使用 log() 函数,这些异常通常不会发生。

使用 JDB 调试器

您可以使用与调试小程序或应用程序相同的 jdb 命令来调试 servlet。

要调试 servlet,我们调试 sun.servlet.http.HttpServer,并仔细观察 HttpServer 如何响应来自浏览器的 HTTP 请求来执行 servlet。这与调试小程序的方式非常相似。不同之处在于,对于小程序,实际被调试的程序是 sun.applet.AppletViewer。

大多数调试器通过自动知道如何调试小程序来隐藏此细节。在它们对 servlet 执行相同操作之前,您必须通过执行以下操作来帮助调试器 −

  • 设置调试器的类路径,以便它可以找到 sun.servlet.http.Http-Server 和相关类。

  • 设置调试器的类路径,以便它还可以找到您的 servlet 和支持类,通常是 server_root/servlets 和 server_root/classes。

您通常不希望 server_root/servlets 出现在类路径中,因为它会禁用 servlet 重新加载。但是,这种包含对于调试很有用。它允许您的调试器在 HttpServer 中的自定义 servlet 加载器加载 servlet 之前在 servlet 中设置断点。

设置正确的类路径后,开始调试 sun.servlet.http.HttpServer。您可以在任何想要调试的 servlet 中设置断点,然后使用 Web 浏览器向给定 servlet 的 HttpServer 发出请求 (http://localhost:8080/servlet/ServletToDebug)。您应该看到执行在断点处停止。

使用注释

代码中的注释可以以各种方式帮助调试过程。注释可以在调试过程中以许多其他方式使用。

Servlet 使用 Java 注释,单行 (// ...) 和多行 (/* ... */) 注释可用于暂时删除 Java 代码的某些部分。如果错误消失,请仔细查看您刚刚注释的代码并找出问题所在。

客户端和服务器标头

有时,当 servlet 的行为不符合预期时,查看原始 HTTP 请求和响应会很有用。如果您熟悉 HTTP 的结构,则可以阅读请求和响应,并准确了解这些标头的具体情况。

重要的调试技巧

以下是有关 servlet 调试的更多调试技巧列表 −

  • 请记住,server_root/classes 不会重新加载,而 server_root/servlets 可能会重新加载。

  • 要求浏览器显示其正在显示的页面的原始内容。这有助于识别格式问题。它通常是"查看"菜单下的一个选项。

  • 通过强制完全重新加载页面,确保浏览器没有缓存先前请求的输出。对于 Netscape Navigator,请使用 Shift-Reload;对于 Internet Explorer,请使用 Shift-Refresh。

  • 验证您的 servlet 的 init() 方法是否接受 ServletConfig 参数并立即调用 super.init(config)。

Servlets - 国际化

在继续之前,让我先解释三个重要术语 −

  • 国际化 (i18n) − 这意味着允许网站提供翻译成访问者语言或国籍的不同版本的内容

  • 本地化 (l10n) − 这意味着向网站添加资源以适应特定的地理或文化区域。

  • 语言环境 − 这是一个特定的文化或地理区域。它通常被称为语言符号,后跟国家/地区符号,中间用下划线分隔。例如,"en_US"代表美国的英语语言环境。

在构建全球网站时,有许多事项需要注意。本教程不会为您提供这方面的完整细节,但它会为您提供一个很好的例子,说明如何通过区分其位置(即语言环境)向互联网社区提供不同语言的网页。

servlet 可以根据请求者的语言环境选择适当的网站版本,并根据当地语言、文化和要求提供适当的网站版本。以下是请求对象的方法,它返回 Locale 对象。

java.util.Locale request.getLocale()

检测语言环境

以下是重要的语言环境方法,您可以使用它们来检测请求者的位置、语言,当然还有语言环境。以下所有方法均显示请求者浏览器中设置的国家名称和语言名称。

Sr.No. 方法 &描述
1

String getCountry()

此方法以 ISO 3166 2 个字母格式返回此语言环境的国家/地区代码(大写)。

2

String getDisplayCountry()

此方法返回适合向用户显示的语言环境国家/地区名称。

3

String getLanguage()

此方法以 ISO 639 格式返回此语言环境的小写语言代码。

4

String getDisplayLanguage()

此方法返回适合向用户显示的语言环境语言名称。

5

String getISO3Country()

此方法返回此语言环境国家/地区的三个字母的缩写。

6

String getISO3Language()

此方法返回此语言环境语言的三个字母的缩写。

示例

此示例显示如何显示请求的语言和相关国家/地区 −

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;

public class GetLocale extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   
      //获取客户端的区域设置
      Locale locale = request.getLocale();
      String language = locale.getLanguage();
      String country = locale.getCountry();

      // 设置响应内容类型
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      String title = "Detecting Locale";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + language + "</h1>" ++
               "<h2 align = \"center\">" + country + "</h2>" ++
         "</body>
         </html>"
      );
   }
} 

语言设置

servlet 可以输出用西欧语言(如英语、西班牙语、德语、法语、意大利语、荷兰语等)编写的页面。在这里,设置 ContentLanguage 标头以正确显示所有字符非常重要。

第二点是使用 HTML 实体显示所有特殊字符,例如,"&#241;"代表"ñ","&#161;"代表"¡",如下所示:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;

public class DisplaySpanish extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // 设置响应内容类型
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      // Set spanish language code.
      response.setHeader("Content-Language", "es");

      String title = "En Espa&ntilde;ol";
      String docType =
      "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1>" + "En Espa&ntilde;ol:" + "</h1>" ++
               "<h1>" + "&iexcl;Hola Mundo!" + "</h1>" ++
            "</body>
         </html>"
      );
   }
} 

特定于语言环境的日期

您可以使用 java.text.DateFormat 类及其静态 getDateTimeInstance() 方法来格式化特定于语言环境的日期和时间。以下示例展示了如何格式化特定于给定语言环境的日期 −

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;
import java.text.DateFormat;
import java.util.Date;

public class DateLocale extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      
      //获取客户端的区域设置
      Locale locale = request.getLocale( );
      String date = DateFormat.getDateTimeInstance(DateFormat.FULL, 
         DateFormat.SHORT, locale).format(new Date( ));

      String title = "Locale Specific Dates";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
     
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + date + "</h1>" ++
            "</body>
         </html>"
      );
   }
} 

特定于语言环境的货币

您可以使用 java.txt.NumberFormat 类及其静态 getCurrencyInstance() 方法以特定于语言环境的货币格式格式化数字(例如 long 或 double 类型)。以下示例展示了如何格式化特定于给定语言环境的货币 −

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;
import java.text.NumberFormat;
import java.util.Date;

public class CurrencyLocale extends HttpServlet {
    
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // 设置响应内容类型
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      //获取客户端的区域设置
      Locale locale = request.getLocale( );
      NumberFormat nft = NumberFormat.getCurrencyInstance(locale);
      String formattedCurr = nft.format(1000000);

      String title = "Locale Specific Currency";
      String docType =
         "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + formattedCurr + "</h1>" ++
            "</body>
         </html>"
      );
   }
} 

特定语言环境的百分比

您可以使用 java.txt.NumberFormat 类及其静态 getPercentInstance() 方法获取特定语言环境的百分比。以下示例展示了如何格式化特定语言环境的百分比 −

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;
import java.text.NumberFormat;
import java.util.Date;

public class PercentageLocale extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
      // 设置响应内容类型
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      
      //获取客户端的区域设置
      Locale locale = request.getLocale( );
      NumberFormat nft = NumberFormat.getPercentInstance(locale);
      String formattedPerc = nft.format(0.51);

      String title = "Locale Specific Percentage";
      String docType =
      "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">";
      
      out.println(docType +
         "<html>" ++
            "<head><title>" + title + "</title></head>" ++
            "<body bgcolor = \"#f0f0f0\">" ++
               "<h1 align = \"center\">" + formattedPerc + "</h1>" ++
            "</body>
         </html>"
      );
   }
} 

Servlet - 注解

到目前为止,您已经了解了 Servlet 如何使用部署描述符(web.xml 文件)将您的应用程序部署到 Web 服务器。Servlet API 3.0 引入了一个名为 javax.servlet.annotation 的新包。它提供了可用于注解 servlet 类的注解类型。如果您使用注解,则不需要部署描述符(web.xml)。但您应该使用 tomcat7 或任何更高版本的 tomcat。

注解可以替换 Web 部署描述符文件(web.xml)中的等效 XML 配置,例如 servlet 声明和 servlet 映射。 Servlet 容器将在部署时处理带注解的类。

Servlet 3.0 中引入的注解类型是 −

Sr.No. 注解和说明
1

@WebServlet

声明一个 servlet。

2

@WebInitParam

指定初始化参数。

3

@WebFilter

声明一个 servlet 过滤器。

4

@WebListener

声明一个 WebListener

5

@HandlesTypes

声明 ServletContainerInitializer 可以处理的类类型。

6

@HttpConstraint

此注解在 ServletSecurity 注解中用于表示要应用于所有 HTTP 协议方法的安全约束,对于这些方法,相应的 HttpMethodConstraint 元素未出现在 ServletSecurity 注解中。

7

@HttpMethodConstraint

此注解在 ServletSecurity 注解中用于表示对特定 HTTP 协议消息的安全约束。

8

@MultipartConfig

可在 Servlet 类上指定的注解,表示 Servlet 的实例需要符合 multipart/form-data MIME 类型的请求。

9

@ServletSecurity

此注解用于 Servlet 实现类,以指定 Servlet 容器对 HTTP 协议消息强制执行的安全约束。

这里我们详细讨论了一些注解。

@WebServlet

@WebServlet 用于声明带有容器的 Servlet 的配置。下表包含用于 WebServlet 注解的属性列表。

Sr.No. 属性 &描述
1

字符串名称

Servlet 的名称

字符串[] 值

URL 模式数组

3

字符串[] urlPatterns

此过滤器适用的 URL 模式数组

4

Int loadOnStartup

整数值为您提供启动排序提示

5

WebInitParam[] initParams

此 Servlet 的初始化参数数组

6

Boolean asyncSupported

此 Servlet 支持的异步操作

7

String smallIcon

此 Servlet 的小图标(如果存在)

8

String largeIcon

此 Servlet 的大图标(如果存在)

9

String description

此 Servlet 的描述(如果存在)

10

String displayName

此 Servlet 的显示名称(如果存在)

必须在注解的 valueurlPattern 属性中声明至少一个 URL 模式,但不能同时声明两者。

当 URL 模式是唯一设置的属性时,建议使用 value 属性,否则应使用 urlPattern 属性。

示例

以下示例描述了如何使用 @WebServlet 注解。这是一个显示文本 Hello Servlet 的简单 servlet。

import java.io.IOException; 
import java.io.PrintWriter; 
import javax.servlet.ServletException; 
import javax.servlet.annotation.WebInitParam; 
import javax.servlet.annotation.WebServlet; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
@WebServlet(value = "/Simple") 
public class Simple extends HttpServlet {

   private static final long serialVersionUID = 1L; 

   protected void doGet(HttpServletRequest request, HttpServletResponse response)  
      throws ServletException, IOException { 
   
      response.setContentType("text/html");   
      PrintWriter out = response.getWriter();   
      out.print("<html><body>");   
      out.print("<h3>Hello Servlet</h3>");   
      out.print("</body></html>");         
   }   
}

以通常的方式编译 Simple.java,并将您的类文件放在 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中。

现在尝试通过运行 http://localhost:8080/Simple 来调用任何 servlet。您将在网页上看到以下输出。

Hello servlet

@WebInitParam

@WebInitParam 注解用于为 Servlet 或 Filter 指定初始化参数。它在 WebFilter 或 WebSevlet 注解中使用。下表包含用于 WebInitParam 注解的属性列表。

Sr.No. 属性 &描述
1

字符串名称

初始化参数的名称

2

字符串值

初始化参数的值

3

字符串描述

初始化参数的描述

示例

以下示例描述了如何将 @WeInitParam 注解与 @WebServlet 一起使用注解。它是一个简单的 servlet,显示文本 Hello Servlet 和字符串值 Hello World!,它们取自 init 参数。

import java.io.IOException; 
import java.io.PrintWriter; 
import javax.servlet.ServletException; 
import javax.servlet.annotation.WebInitParam; 
import javax.servlet.annotation.WebServlet; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse;

@WebServlet(value = "/Simple", initParams = { 
   @WebInitParam(name = "foo", value = "Hello "), 
   @WebInitParam(name = "bar", value = " World!") 
}) 
public class Simple extends HttpServlet {

   private static final long serialVersionUID = 1L; 

   protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {   
      
      response.setContentType("text/html");   
      PrintWriter out = response.getWriter();   
      out.print("<html><body>");   
      out.print("<h3>Hello Servlet</h3>");   
      out.println(getInitParameter("foo")); 
      out.println(getInitParameter("bar")); 
      out.print("</body></html>");         
   }   
}

以通常的方式编译 Simple.java,并将您的类文件放入 <Tomcat-installationdirectory>;/webapps/ROOT/WEB-INF/classes。

现在尝试通过运行 http://localhost:8080/Simple 来调用任何 servlet。您将在网页上看到以下输出。

Hello Servlet

Hello World!

@Webfilter

这是用于声明 servlet 过滤器的注解。它在部署时由容器处理,并将相应的过滤器应用于指定的 URL 模式、servlet 和调度程序类型。

@WebFilter 注解定义 Web 应用程序中的过滤器。此注解在类上指定,并包含有关正在声明的过滤器的元数据。注解的过滤器必须至少指定一个 URL 模式。下表列出了用于 WebFilter 注解的属性。

Sr.No. 属性 &描述
1

String filterName

过滤器的名称

String[] urlPatterns

提供过滤器适用的值或 urlPatterns 数组

3

DispatcherType[] dispatcherTypes

指定过滤器适用的调度程序类型(请求/响应)

4

String[] servletNames

提供一个 servlet 名称数组

5

String displayName

过滤器的名称

6

String description

过滤器的描述

7

WebInitParam[] initParams

此过滤器的初始化参数数组

8

Boolean asyncSupported

此过滤器支持的异步操作

9

String smallIcon

此过滤器的小图标(如果存在)

10

String largeIcon

此过滤器的大图标(如果存在)

示例

以下示例描述了如何使用 @WebFilter 注解。这是一个简单的 LogFilter,它在控制台上显示 Init-param test-param 的值和当前时间戳。这意味着,过滤器就像请求和响应之间的接口层一样工作。这里我们使用"/*"作为 urlPattern。这意味着,此过滤器适用于所有 servlet。

import java.io.IOException; 
import javax.servlet.annotation.WebFilter; 
import javax.servlet.annotation.WebInitParam; 
import javax.servlet.*; 
import java.util.*;  

// 实现 Filter 类

@WebFilter(urlPatterns = {"/*"}, initParams = { 
   @WebInitParam(name = "test-param", value = "Initialization Paramter")}) 
public class LogFilter implements Filter {
   
   public void init(FilterConfig config) throws ServletException { 
      // 获取初始化参数
      String testParam = config.getInitParameter("test-param");
            
      // 打印 init 参数
      System.out.println("Test Param: " + testParam);  
   } 

   public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException { 
	  
      // 记录当前时间戳。
      System.out.println("Time " + new Date().toString());  
         
      // 将请求传递回过滤链
      chain.doFilter(request,response); 
   }

   public void destroy( ) {
      /* Called before the Filter instance is removed  
      from service by the web container*/ 
   } 
}

以通常的方式编译 Simple.java,并将您的类文件放在 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中。

现在尝试通过运行 http://localhost:8080/Simple 来调用任何 servlet。您将在网页上看到以下输出。

Hello Servlet

Hello World!

现在,打开 servlet 控制台。在那里,您将找到 init 参数 testparam 的值和 当前时间戳 以及 servlet 通知消息。