Servlets 简明教程

Servlets - Quick Guide

Servlets - Overview

What are Servlets?

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

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

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

  1. Performance is significantly better.

  2. Servlet 在 Web 服务器的地址空间内执行。无需创建单独的进程来处理每个客户端请求。

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

  4. 服务器上的 Java 安全管理器会实施一组限制来保护服务器机器上的资源。因此 Servlet 是受信任的。

  5. Servlet 可以使用 Java 类库的全部功能。它可以通过您已经了解的套接字和 RMI 机制与 applet、数据库或其他软件进行通信。

Servlets Architecture

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

servlet arch

Servlets Tasks

Servlet 执行以下主要任务 -

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

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

  3. 处理数据并生成结果。此过程可能需要与数据库交谈,执行 RMI 或 CORBA 调用,调用 Web 服务或直接计算响应。

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

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

Servlets Packages

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

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

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

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

What is Next?

我将一步步带您设置环境以开始使用 Servlet。因此,系好安全带,享受与 Servlet 的驾驶乐趣。我相信您会非常喜欢本教程。

Servlets - Environment Setup

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

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

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

Setting up Java Development Kit

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

您可从 Oracle 的 Java 网站下载 SDK− Java SE Downloads

下载 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 值并按“确定”按钮。

在 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 的位置。

Setting up Web Server − Tomcat

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

Apache Tomcat 是 Java Servlet 和 Java Server Pages 技术的一个开源软件实现,可以充当 servlet 的独立服务器,也可以与 Apache Web 服务器集成。以下是在计算机上设置 Tomcat 的步骤:

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

  2. 下载安装程序后,将二进制分发文件解压到方便的位置。例如,在 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
or
C:\apache-tomcat-8.0.28\bin\startup.bat

可以通过在 Unix(Solaris、Linux 等)计算机上执行以下命令启动 Tomcat:

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

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

tomcathomepage

有关配置和运行 Tomcat 的更多信息,请参阅此处附带的文档以及 Tomcat 网站:[role="bare"] [role="bare"]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

Setting Up the CLASSPATH

由于 servlet 不是 Java Platform, Standard Edition 的一部分,因此必须向编译器标识 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

NOTE − 假设您的开发目录是 C:\ServletDevel(Windows)或 /usr/ServletDevel(Unix),则您需要以类似于上面添加的方式将这些目录添加到 CLASSPATH 中。

Servlets - Life Cycle

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

  1. 通过调用 init() 方法对 servlet 进行初始化。

  2. servlet 调用 service() 方法来处理客户端的请求。

  3. @ {s0} 方法调用后,servlet 被终止。

  4. 最后,servlet 由 JVM 的垃圾回收器回收。

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

The init() Method

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

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

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

init 方法的定义如下:

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

The service() Method

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

每次服务器收到对 servlet 的请求时,服务器都会生成一个新线程并调用 service。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() 是各个 service 请求中使用最多的方法。以下是这两个方法的签名。

The doGet() Method

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

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

The doPost() Method

POST 请求来自明确列出 POST 为 METHOD 的 HTML 表单,应当由 doPost() 方法处理。

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

The destroy() Method

destroy() 方法仅在 servlet 生命周期的末尾调用一次。此方法可以让你的 servlet 有机会关闭数据库连接、停止后台线程、将 cookie 列表或点击数写入磁盘,以及执行其他此类清理活动。

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

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

Architecture Diagram

下图描绘了一个典型的 servlet 生命周期场景。

  1. 首先,到达服务器的 HTTP 请求会被委派给 servlet 容器。

  2. 在调用 service() 方法之前,servlet 容器会加载 servlet。

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

servlet lifecycle

Servlets - Examples

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

Sample Code

以下为显示 Hello World 的 servlet 示例的示例源代码结构 -

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class HelloWorld extends HttpServlet {

   private String message;

   public void init() throws ServletException {
      // Do required initialization
      message = "Hello World";
   }

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

      // Set response content type
      response.setContentType("text/html");

      // Actual logic goes here.
      PrintWriter out = response.getWriter();
      out.println("<h1>" + message + "</h1>");
   }

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

Compiling a 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 Deployment

默认情况下,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 中,并创建位于 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/ 中 web.xml 文件中的以下条目

<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-app>…​</web-app> web.xml 文件内。此表格中可能已经存在各种条目,但不必担心。

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

servlet example

Servlets - Form Data

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

GET Method

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

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

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

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

POST Method

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

Reading Form Data using Servlet

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

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

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

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

GET Method Example using URL

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

下面是 HelloForm.java servlet 程序来处理 web 浏览器给定的输入。我们准备使用 getParameter() 方法,该方法使得访问传递的信息变得非常容易 −

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class HelloForm extends HttpServlet {

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

      // Set response content type
      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\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<ul>\n" +
                  "  <li><b>First Name</b>: "
                  + request.getParameter("first_name") + "\n" +
                  "  <li><b>Last Name</b>: "
                  + request.getParameter("last_name") + "\n" +
               "</ul>\n" +
            "</body>" +
         "</html>"
      );
   }
}

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

$ javac HelloForm.java

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

<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>

现在在浏览器的“位置:”框中键入 [role="bare"] [role="bare"]http://localhost:8080/HelloForm?first_name=ZARA&last_name=ALI ,在浏览器中执行上述命令之前,请确保您已经启动了 tomcat 服务器。这将生成以下结果 −

Using GET Method to Read Form Data

First Name: ZARA
Last Name: ALI

GET Method Example Using Form

这是一个简单的示例,它使用 HTML FORM 和提交按钮传递两个值。我们将使用相同的 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 目录中。当您访问 [role="bare"] [role="bare"]http://localhost:8080/Hello.htm 时,以下就是上述表单的实际输出。

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

POST Method Example Using Form

让我们对上述 servlet 进行一点修改,以便它可以处理 GET 和 POST 方法。以下是 HelloForm.java servlet 程序来处理使用 GET 或 POST 方法由 web 浏览器给定的输入。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class HelloForm extends HttpServlet {

   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set response content type
      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\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<ul>\n" +
                  "  <li><b>First Name</b>: "
                  + request.getParameter("first_name") + "\n" +
                  "  <li><b>Last Name</b>: "
                  + request.getParameter("last_name") + "\n" +
               "</ul>\n" +
            "</body>"
         "</html>"
      );
   }

   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      doGet(request, response);
   }
}

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

<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 运行的本地计算机上的结果。

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

Passing Checkbox Data to Servlet Program

如果要求选择多个选项,则使用复选框。

这是一个带两个复选框的表单的 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>

此代码的结果是以下表单

下面是 CheckBox.java servlet 程序来处理复选框按钮的 web 浏览器给定的输入。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class CheckBox extends HttpServlet {

   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set response content type
      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\">\n";

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

   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      doGet(request, response);
   }
}

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

Reading Checkbox Data

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

Reading All Form Parameters

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

获得枚举后,我们可以使用 hasMoreElements() 方法来确定何时停止,并使用 nextElement() 方法来获取每个参数名称,从而以标准方式向下遍历枚举。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Extend HttpServlet class
public class ReadParams extends HttpServlet {

   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set response content type
      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\">\n";

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

      Enumeration paramNames = request.getParameterNames();

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

         // Read single valued data
         if (paramValues.length == 1) {
            String paramValue = paramValues[0];
            if (paramValue.length() == 0)
               out.println("<i>No Value</i>");
               else
               out.println(paramValue);
         } else {
            // Read multiple valued data
            out.println("<ul>");

            for(int i = 0; i < paramValues.length; i++) {
               out.println("<li>" + paramValues[i]);
            }
            out.println("</ul>");
         }
      }
      out.println("</tr>\n</table>\n</body></html>");
   }

   // Method to handle POST method request.
   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 将生成以下结果 −

Reading All Form Parameters


Param Name
Param Value(s)


maths
on


chemistry
on

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

Servlets - Client HTTP Request

当浏览器请求一个网页时,它会向 Web 服务器发送很多无法直接读取的信息,因为这些信息作为 HTTP 请求标头的组成部分进行传输。您可以参阅 HTTP Protocol 以获得更多相关信息。

以下是来自浏览器端的重要标头信息,您将在 Web 编程中非常频繁地使用它们 −

Sr.No.

Header & Description

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

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

6

Connection 此标头表明客户端是否可以处理持久性 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 此标头标识提出请求的浏览器或其他客户端,并且可用于将不同的内容返回到不同类型的浏览器。

Methods to read HTTP Header

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

Sr.No.

Method & Description

1

Cookie[] getCookies() 返回一个包含客户端随此请求发送的所有 Cookie 对象的数组。

2

Enumeration getAttributeNames() 返回一个包含此请求中可用的属性名称的枚举。

3

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

4

Enumeration getParameterNames() 返回一个包含此请求中包含的参数名称的 String 对象的枚举

5

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

6

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

7

Locale getLocale() 根据接受语言标头,返回客户端将接受内容的首选语言环境。

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) *以 String 形式返回指定请求标头值。

15

*String getMethod() *返回此请求所用 HTTP 方法的名称,例如,GET、POST 或 PUT。

16

String getParameter(String name) 以 String 形式返回请求参数的值,如果没有此参数,则返回 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() *返回第一行 HTTP 请求中从协议名称到查询字符串部分的请求 URL 的部分。

24

String getRequestedSessionId() 返回客户端指定的会话 ID。

25

*String getServletPath() *返回调用 JSP 的请求 URL 的部分。

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 Header Request Example

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

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

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Extend HttpServlet class
public class DisplayHeader extends HttpServlet {

   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set response content type
      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\">\n";

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

      Enumeration headerNames = request.getHeaderNames();

      while(headerNames.hasMoreElements()) {
         String paramName = (String)headerNames.nextElement();
         out.print("<tr><td>" + paramName + "</td>\n");
         String paramValue = request.getHeader(paramName);
         out.println("<td> " + paramValue + "</td></tr>\n");
      }
      out.println("</table>\n</body></html>");
   }

   // Method to handle POST method request.
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      doGet(request, response);
   }
}

现在,调用上面的 servlet 将生成以下结果:

HTTP Header Request Example


Header Name
Header Value(s)


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

Servlets - Server HTTP Response

如前一章所述,当 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.

Header & Description

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 此标头应与所有状态代码位于 300s 中的响应一起包含。这会通知浏览器文档地址。浏览器自动重新连接到此位置并检索新文档。

12

Refresh 此标头指定浏览器应多久请求更新页面。您可以在页面刷新后的秒数中指定时间。

13

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

14

Set-Cookie 此标头指定与页面关联的 Cookie。

Methods to Set HTTP Response Header

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

Sr.No.

Method & Description

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 Header Response Example

您已经在前面的示例中看到 setContentType() 方法正在工作,并且后面的示例也会使用同样的方法,此外,我们会使用 setIntHeader() 方法来设置 Refresh 头。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Extend HttpServlet class
public class Refresh extends HttpServlet {

   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set refresh, autoload time as 5 seconds
      response.setIntHeader("Refresh", 5);

      // Set response content type
      response.setContentType("text/html");

      // Get current time
      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\">\n";

      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n"+
         "<body bgcolor = \"#f0f0f0\">\n" +
         "<h1 align = \"center\">" + title + "</h1>\n" +
         "<p>Current Time is: " + CT + "</p>\n"
      );
   }

   // Method to handle POST method request.
   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 Status Codes

HTTP 请求和 HTTP 响应消息的格式是相似的,并且将具有以下结构 -

  1. 初始状态行 + CRLF(回车 + 换行,即新行)

  2. 零或多个头行 + CRLF

  3. 一个空行,即 CRLF

  4. 一个可选的消息主体,例如文件、查询数据或查询输出。

例如,一个服务器响应头如下所示 -

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

The server switches protocol.

200

OK

The request is 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 协议”版本。

Methods to Set HTTP Status Code

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

Sr.No.

Method & Description

1

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

2

public void sendRedirect(String url) 该方法生成一个 302 响应以及一个 Location 头,提供新文档的 URL。

3

public void sendError(int code, String message) 该方法发送一个状态代码(通常是 404),以及一条短消息,该消息会自动格式化在 HTML 文档中并发送给客户端。

HTTP Status Code Example

以下是向客户端浏览器发送 407 错误代码的示例,浏览器将向您显示“需要授权!!!”消息。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Extend HttpServlet class
public class showError extends HttpServlet {

   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set error code and reason.
      response.sendError(407, "Need authentication!!!" );
   }

   // Method to handle POST method request.
   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

Servlets - Writing Filters

Servlet 过滤器是可以在 Servlet 编程中用于以下目的的 Java 类 −

  1. 在从客户端访问后端资源之前拦截请求。

  2. 在将响应从服务器发回客户端之前进行处理。

规范建议了多种类型的过滤器 −

  1. Authentication Filters.

  2. Data compression Filters.

  3. Encryption Filters.

  4. 触发资源访问事件的过滤器。

  5. Image Conversion Filters.

  6. Logging and Auditing Filters.

  7. MIME-TYPE Chain Filters.

  8. Tokenizing Filters .

  9. 转化 XML 内容的 XSL/T 过滤器。

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

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

Servlet Filter Methods

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

Sr.No.

Method & Description

1

public void doFilter (ServletRequest, ServletResponse, FilterChain) 当请求/响应对因客户端对链尾部的资源发出请求而通过链传递时,此方法由容器调用。

2

public void init(FilterConfig filterConfig) Web 容器调用此方法以向过滤器表示它正在被投入使用。

3

public void destroy() 当 Web 容器告知过滤器即将停用时,调用此方法。

Servlet Filter − Example

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

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Implements Filter class
public class LogFilter implements Filter  {
   public void  init(FilterConfig config) throws ServletException {

      // Get init parameter
      String testParam = config.getInitParameter("test-param");

      //Print the init parameter
      System.out.println("Test Param: " + testParam);
   }

   public void  doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws java.io.IOException, ServletException {

      // Get the IP address of client machine.
      String ipAddress = request.getRemoteAddr();

      // Log the IP address and current timestamp.
      System.out.println("IP "+ ipAddress + ", Time " + new Date().toString());

      // Pass request back down the filter chain
      chain.doFilter(request,response);
   }

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

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

Servlet Filter Mapping in Web.xml

过滤器经过定义并映射到 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 记录器将上述日志记录到单独文件中。

Using Multiple Filters

您的 Web 应用程序可能针对特定目的定义多个不同的过滤器。假设您定义了两个过滤器 AuthenFilter 和 LogFilter。除了需要创建不同的映射外(如下所述),其余过程将保持上述说明 −

<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>

Filters Application Order

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

例如,上述示例将首先应用 LogFilter,然后将其应用任何 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>

Servlets - Exception Handling

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

您需要在 web.xml 中使用 error-page 元素,以指定根据特定 exceptions 或 HTTP status codes 来调用 servlet。

web.xml Configuration

假设您有一个 ErrorHandler 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,而不是针对每个异常定义单独的 error-page 元素 −

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

以下是在 Exception Handling 的 web.xml 中要注意的要点 −

  1. servlet ErrorHandler 以与任何其他 servlet 相同的方式定义,并在 web.xml 中进行配置。

  2. 如果存在状态代码为 404(未找到)或 403(禁止)的任何错误,则将调用 ErrorHandler servlet。

  3. 如果 Web 应用程序引发 ServletException 或 IOException,则 Web 容器将调用 /ErrorHandler servlet。

  4. 您可以定义不同的 Error Handler 来处理不同类型的错误或异常。上述示例具有很强的通用性,希望有助于向您解释基本概念。

Request Attributes − Errors/Exceptions

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

Sr.No.

Attribute & Description

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 数据类型中并进行分析后存储。

Error Handler Servlet Example

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

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Extend HttpServlet class
public class ErrorHandler extends HttpServlet {

   // Method to handle GET method request.
   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";
      }

      // Set response content type
      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\">\n";

      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n" +
         "<body bgcolor = \"#f0f0f0\">\n");

      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>");
   }

   // Method to handle POST method request.
   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 Handling

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

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

  1. 服务器脚本将一组 Cookie 发送到浏览器。例如,姓名、年龄或身份证号等。

  2. 浏览器在本地计算机上存储此信息以供将来使用。

  3. 当浏览器下次向 Web 服务器发送任何请求时,它会将那些 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 Methods

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

Sr.No.

Method & Description

1

*public void setDomain(String pattern) *此方法设置 cookie 适用的域名,例如 tutorialspoint.com。

2

*public String getDomain() *此方法获取 cookie 适用的域名,例如 tutorialspoint.com。

3

public void setMaxAge(int expiry) 此方法设置 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 适用的路径。如果你未指定路径,该 Cookie 会返回与当前页面同目录中所有 URL 以及所有子目录。

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。

Setting Cookies with Servlet

通过 Servlet 设置 Cookie 涉及三个步骤 −

(1) Creating a Cookie object − 使用 Cookie 名称和 Cookie 值调用 Cookie 构造函数,两者均为字符串。

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

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

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

(2) Setting the maximum age − 使用 setMaxAge 指定 Cookie 应有效多长时间(以秒为单位)。以下将设置一个 24 小时的 Cookie。

cookie.setMaxAge(60 * 60 * 24);

(3) Sending the Cookie into the HTTP response headers − 您使用 response.addCookie 在 HTTP 响应头中添加 cookie,如下所示:

response.addCookie(cookie);

Example

我们修改我们的 Form Example 以设置 first 和 last name 的 cookie。

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class HelloForm extends HttpServlet {

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

      // Create cookies for first and last names.
      Cookie firstName = new Cookie("first_name", request.getParameter("first_name"));
      Cookie lastName = new Cookie("last_name", request.getParameter("last_name"));

      // Set expiry date after 24 Hrs for both the cookies.
      firstName.setMaxAge(60*60*24);
      lastName.setMaxAge(60*60*24);

      // Add both the cookies in the response header.
      response.addCookie( firstName );
      response.addCookie( lastName );

      // Set response content type
      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\">\n";

      out.println(docType +
         "<html>\n" +
            "<head>
               <title>" + title + "</title>
            </head>\n" +

            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<ul>\n" +
                  "  <li><b>First Name</b>: "
                  + request.getParameter("first_name") + "\n" +
                  "  <li><b>Last Name</b>: "
                  + request.getParameter("last_name") + "\n" +
               "</ul>\n" +
            "</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 安装目录>/webapps/ROOT 目录中。在您访问时 [role="bare"] [role="bare"]http://localhost:8080/Hello.htm ,以下是上面表单的实际输出。

尝试输入 First Name 和 Last Name,然后点击提交按钮。将显示您的屏幕上的 first name 和 last name,同时将设置两个 cookies(firstName 和 lastName),下次按下提交按钮时将传回服务器。

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

Reading Cookies with Servlet

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

Example

我们读取在先前示例中设置的 cookie −

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
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();

      // Set response content type
      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\">\n";

      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n" +
         "<body bgcolor = \"#f0f0f0\">\n" );

      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”,则运行 [role="bare"] [role="bare"]http://localhost:8080/ReadCookies 将显示以下结果 −

 Found Cookies Name and Value
Name : first_name, Value: John
Name : last_name,  Value: Player

Delete Cookies with Servlet

删除 cookie 非常简单。如果您希望删除 cookie,只需按照以下三个步骤操作:

  1. 读取已存在的 cookie 并将其存储到 Cookie 对象中。

  2. 使用 setMaxAge() 方法将 cookie age 设置为 0,以删除现有 cookie

  3. 将此 cookie 添加回响应头中。

Example

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

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class DeleteCookies 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();

      // Set response content type
      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\">\n";

      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n" +
         "<body bgcolor = \"#f0f0f0\">\n" );

      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 文件中的相应条目。现在,运行 [role="bare"] [role="bare"]http://localhost:8080/DeleteCookies 将显示以下结果 −

Cookies Name and Value
Deleted cookie : first_name
Name : first_name, Value: John
Name : last_name,  Value: Player

现在,尝试运行 [role="bare"] [role="bare"]http://localhost:8080/ReadCookies ,它将只显示一个如下所示的 cookie −

 Found Cookies Name and Value
Name : last_name,  Value: Player

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

Servlets - Session Tracking

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

仍然有以下三种方法来维持 Web 客户端与 Web 服务器之间的会话:

Cookies

Web 服务器可以为每个网络客户端分配一个作为 cookie 的唯一会话 ID,并且在接收到 cookie 后,可用于识别客户的后续请求。

这可能不是一个有效的方法,因为很多时候浏览器不支持 cookie,因此我建议不要使用这些步骤来维护会话。

Hidden Form Fields

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

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

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

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

URL Rewriting

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

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

URL 重写是维护会话的更好方法,即使浏览器不支持 cookie,它也能发挥作用。URL 重写的缺点是即使对于简单的静态 HTML 页面,你也必须动态生成每个 URL 以分配会话 ID。

The HttpSession Object

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

servlet 容器使用此接口来创建 HTTP 客户端与 HTTP 服务器之间的会话。会话会在一个指定的时间段内保持,跨多个来自用户的连接或页面请求。

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

HttpSession session = request.getSession();

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

Sr.No.

Method & Description

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 容器将此会话无效化之前,两次客户端请求之间的时间(秒)。

Session Tracking Example

此示例描述了如何使用 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 {

      // Create a session object if it is already not  created.
      HttpSession session = request.getSession(true);

      // Get session creation time.
      Date createTime = new Date(session.getCreationTime());

      // Get last access time of this web page.
      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");

      // Check if this is new comer on your web page.
      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);

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

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

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +

            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<h2 align = \"center\">Session Infomation</h2>\n" +
               "<table border = \"1\" align = \"center\">\n" +

                  "<tr bgcolor = \"#949494\">\n" +
                     "  <th>Session info</th><th>value</th>
                  </tr>\n" +

                  "<tr>\n" +
                     "  <td>id</td>\n" +
                     "  <td>" + session.getId() + "</td>
                  </tr>\n" +

                  "<tr>\n" +
                     "  <td>Creation Time</td>\n" +
                     "  <td>" + createTime + "  </td>
                  </tr>\n" +

                  "<tr>\n" +
                     "  <td>Time of Last Access</td>\n" +
                     "  <td>" + lastAccessTime + "  </td>
                  </tr>\n" +

                  "<tr>\n" +
                     "  <td>User ID</td>\n" +
                     "  <td>" + userID + "  </td>
                  </tr>\n" +

                  "<tr>\n" +
                     "  <td>Number of visits</td>\n" +
                     "  <td>" + visitCount + "</td>
                  </tr>\n" +
               "</table>\n" +
            "</body>
         </html>"
      );
   }
}

编译上面的 servlet SessionTrack 并创建 web.xml 文件中适当的条目。现在运行 [role="bare"] [role="bare"]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

Deleting Session Data

完成用户的会话数据时,您有几种选择 −

  1. Remove a particular attribute − 可以调用 public void removeAttribute(String name) 方法以删除与特定键关联的值。

  2. Delete the whole session − 可以调用 public void invalidate() 方法以放弃整个会话。

  3. Setting Session timeout − 可以调用 public void setMaxInactiveInterval(int interval) 方法来单独设置会话超时。

  4. Log the user out − 支持 servlet 2.4 的服务器,您可以调用 logout 让客户端从 Web 服务器注销并使属于所有用户的会话无效。

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

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

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

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

Servlets - Database Access

本教程假定您了解 JDBC 应用程序的工作原理。在使用 servlet 访问数据库之前,确保您已经设置好了适当的 JDBC 环境以及数据库。

要详细了解如何使用 JDBC 及其环境设置访问数据库,可以参阅我们的 JDBC Tutorial

首先了解基本概念,我们要创建一个简单表格并在该表格中创建一些记录,如下所示 −

Create Table

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

Step 1

打开 Command Prompt 并更改到安装目录,如下所示 −

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

Step 2

按照以下方法登录数据库:

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

Step 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>

Create Data Records

最后,在 Employee 表中创建一些记录,如下所示 −

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>

Accessing a Database

以下示例显示了如何使用 Servlet 访问 TEST 数据库:

// Loading required libraries
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 driver name and database URL
      static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
      static final String DB_URL="jdbc:mysql://localhost/TEST";

      //  Database credentials
      static final String USER = "root";
      static final String PASS = "password";

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      String title = "Database Result";

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

      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n" +
         "<body bgcolor = \"#f0f0f0\">\n" +
         "<h1 align = \"center\">" + title + "</h1>\n");
      try {
         // Register JDBC driver
         Class.forName("com.mysql.jdbc.Driver");

         // Open a connection
         Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);

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

         // Extract data from result set
         while(rs.next()){
            //Retrieve by column name
            int id  = rs.getInt("id");
            int age = rs.getInt("age");
            String first = rs.getString("first");
            String last = rs.getString("last");

            //Display values
            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) {
         //Handle errors for JDBC
         se.printStackTrace();
      } catch(Exception e) {
         //Handle errors for Class.forName
         e.printStackTrace();
      } finally {
         //finally block used to close resources
         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 [role="bare"] [role="bare"]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

Servlets - File Uploading

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

Creating a File Upload Form

以下 HTM 代码创建一个上传程序表单。以下是需要注意的重要事项:

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

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

  3. 表单 action 属性应设置为一个 servlet 文件,它将处理后端服务器上的文件上传。以下示例使用 UploadServlet servlet 上传文件。

  4. 要上传单个文件,应使用具有属性 type="file" 的单个 <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 中选择一个文件,当用户单击“上传文件”时,表单将与所选文件一起提交:

File Upload:
Select a file to upload:




NOTE: This is just dummy form and would not work.

Writing Backend Servlet

以下是 UploadServlet servlet,它将负责接受上传的文件并将其存储在目录 <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 的源代码,它可以一次处理多个文件上传。在继续之前,请确保以下内容:

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

  2. FileUpload 依赖于 Commons IO,因此请确保你的 classpath 中有最新版本的 commons-io-x.x.jar 文件。你可以从 https://commons.apache.org/io/ 下载它。

  3. 在测试以下示例时,应上传一个大小小于 maxFileSize 的文件,否则文件将不会上传。

  4. 确保你已经提前创建了目录 c:\temp 和 c:\apache-tomcat8.0.28\webapps\data。

// Import required java libraries
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( ){
      // Get the file location where it would be stored.
      filePath = getServletContext().getInitParameter("file-upload");
   }

   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, java.io.IOException {

      // Check that we have a file upload request
      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();

      // maximum size that will be stored in memory
      factory.setSizeThreshold(maxMemSize);

      // Location to save data that is larger than maxMemSize.
      factory.setRepository(new File("c:\\temp"));

      // Create a new file upload handler
      ServletFileUpload upload = new ServletFileUpload(factory);

      // maximum file size to be uploaded.
      upload.setSizeMax( maxFileSize );

      try {
         // Parse the request to get file items.
         List fileItems = upload.parseRequest(request);

         // Process the uploaded file items
         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 () ) {
               // Get the uploaded file parameters
               String fieldName = fi.getFieldName();
               String fileName = fi.getName();
               String contentType = fi.getContentType();
               boolean isInMemory = fi.isInMemory();
               long sizeInBytes = fi.getSize();

               // Write the file
               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.");
      }
   }
}

Compile and Running 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 表单上传文件。当您尝试 [role="bare"] [role="bare"]http://localhost:8080/UploadFile.htm 时,它将显示以下结果,这将帮助您上传本地计算机上的任何文件。

File Upload:
Select a file to upload:

如果您的 servlet 脚本运行良好,您的文件应上传到 c:\apache-tomcat-8.0.28\webapps\data\ 目录中。

Servlets - Handling Date

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

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

Date( )

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

Date(long millisec)

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

Sr.No.

Methods & Description

1

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

2

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

3

Object clone( ) 复制调用 Date 对象。

4

int compareTo(Date date) 将调用对象的值与日期进行比较。如果值相等,则返回 0。如果调用对象早于日期,则返回一个负值。如果调用对象晚于日期,则返回一个正值。

5

int compareTo(Object obj) 如果 obj 是 Date 类,则与 compareTo(Date) 的操作相同。否则,它会抛出一个 ClassCastException。

6

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

7

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

8

int hashCode( ) 返回调用对象的哈希代码。

9

void setTime(long time) 将时间和日期设置为由时间指定的值,该时间表示自 1970 年 1 月 1 日午夜以来的経過时间(以毫秒为单位)。

10

String toString( ) 将调用 Date 对象转换为一个字符串并返回结果。

Getting Current Date & Time

在 Java Servlet 中获取当前日期和时间非常简单。您可以使用一个简单的 Date 对象和 toString() 方法来按如下方式打印当前日期和时间 -

// Import required java libraries
import java.io.*;
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class CurrentDate extends HttpServlet {

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

      // Set response content type
      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\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<h2 align = \"center\">" + date.toString() + "</h2>\n" +
            "</body>
         </html>"
      );
   }
}

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

Display Current Date & Time
Mon Jun 21 21:46:49 GMT+04:00 2010

尝试刷新 URL [role="bare"] [role="bare"]http://localhost:8080/CurrentDate ,每次刷新您都会发现几秒钟的差异。

Date Comparison

如上所述,您可以在 Servlet 中使用所有可用的 Java 方法。如果您需要比较两个日期,以下是方法:

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

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

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

Date Formatting using SimpleDateFormat

SimpleDateFormat 是一个用于以特定语言敏感的方式设置和解析日期的具体类。使用 SimpleDateFormat,可以选择任何用户定义的模式来设置日期和时间格式。

让我们修改上面的示例,如下所示:

// Import required java libraries
import java.io.*;
import java.text.*;
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.*;

// Extend HttpServlet class
public class CurrentDate extends HttpServlet {

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

      // Set response content type
      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\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<h2 align = \"center\">" + ft.format(dNow) + "</h2>\n" +
            "</body>
         </html>"
      );
   }
}

再次编译上面的 servlet,然后使用 URL [role="bare"] [role="bare"]http://localhost:8080/CurrentDate 调用此 servlet。这将产生以下结果:

Display Current Date & Time
Mon 2010.06.21 at 10:06:44 PM GMT+04:00

Simple DateFormat Format Codes

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

Character

Description

Example

G

Era designator

AD

y

Year in four digits

2001

M

Month in year

July or 07

d

Day in month

10

h

Hour in A.M./P.M. (1~12)

12

H

Hour in day (0~23)

22

m

Minute in hour

30

s

Second in minute

55

S

Millisecond

234

E

Day in week

Tuesday

D

Day in year

360

F

本月的星期几

2(七月的第二个星期三)

w

Week in year

40

W

Week in month

1

a

A.M./P.M. marker

PM

k

Hour in day (1~24)

24

K

Hour in A.M./P.M. (0~11)

10

z

Time zone

Eastern Standard Time

'

Escape for text

Delimiter

"

Single quote

`

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

Servlets - Page Redirection

页面重定向是一种将客户端发送到不同于请求的新位置的技术。当一个文档移到一个新位置或可能因为负载平衡时,通常使用页面重定向。

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

public void HttpServletResponse.sendRedirect(String location)
throws IOException

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

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

Example

此示例演示了 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 {

      // Set response content type
      response.setContentType("text/html");

      // New location to be redirected
      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 [role="bare"] [role="bare"]http://localhost:8080/PageRedirect 调用此 servlet。这将重定向您到 URL [role="bare"] [role="bare"]http://www.photofuntoos.com

Servlets - Hits Counter

Hit Counter for a Web Page

很多情况下,您会希望了解网站特定页面上的点击总数。使用 servlet 计算这些点击数非常简单,因为 servlet 的生命周期由运行其的容器来控制。

以下是实现基于 Servlet 生命周期计数点击数的简单方法:

  1. 在 init() 方法中初始化一个全局变量。

  2. 每次调用 doGet() 或 doPost() 方法时,将全局变量加 1。

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

  4. 如果您只想统计会话中唯一页面点击数,那么可以使用 isNew() 方法来检查会话中是否已经点击过相同的页面。此步骤是可选的。

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

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

Example

本示例演示如何实现简单的页面访问计数器 -

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() {
      // Reset hit counter.
      hitCount = 0;
   }

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

      // Set response content type
      response.setContentType("text/html");

      // This method executes whenever the servlet is hit
      // increment hitCount
      hitCount++;
      PrintWriter out = response.getWriter();
      String title = "Total Number of Hits";
      String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<h2 align = \"center\">" + hitCount + "</h2>\n" +
            "</body>
         </html>"
      );
   }

   public void destroy() {
      // This is optional step but if you like you
      // can write hitCount value in your database.
   }
}

现在我们编译上述 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 [role="bare"] [role="bare"]http://localhost:8080/PageHitCounter 调用此 servlet。每次刷新此页面,计数器都会增加 1,并且会显示以下结果 -

Total Number of Hits
6
Hit Counter for a Website:

许多时候您会希望了解整个网站上的访问总数。这在 Servlet 中也很简单,我们可以使用过滤器来实现此目的。

以下是实现基于过滤器生命周期的简单网站访问计数器的步骤 -

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

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

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

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

Example

本示例演示如何实现简单的网站访问计数器 -

// Import required java libraries
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 {
      // Reset hit counter.
      hitCount = 0;
   }

   public void  doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws java.io.IOException, ServletException {

      // increase counter by one
      hitCount++;

      // Print the counter.
      System.out.println("Site visits count :"+ hitCount );

      // Pass request back down the filter chain
      chain.doFilter(request,response);
   }

   public void destroy() {
      // This is optional step but if you like you
      // can write hitCount value in your database.
   }
}

现在让我们编译上述 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 [role="bare"] [role="bare"]http://localhost:8080/ )。每次任何页面被访问时,计数器就会增加 1,并且会在日志中显示以下消息 -

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

Servlets - Auto Page Refresh

考虑一个显示现场比赛比分、股票市场状况或汇率的网页。对于所有这些类型的页面,您都需要使用浏览器的刷新或重新加载按钮定期刷新您的网页。

Java Servlet 通过为您提供一种机制来简化此项工作,您可以在这种机制中以这样的方式制作网页,使其在给定的时间间隔后自动刷新。

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

public void setIntHeader(String header, int headerValue)

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

Auto Page Refresh Example

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

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

// Extend HttpServlet class
public class Refresh extends HttpServlet {

   // Method to handle GET method request.
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

      // Set refresh, autoload time as 5 seconds
      response.setIntHeader("Refresh", 5);

      // Set response content type
      response.setContentType("text/html");

      // Get current time
      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\">\n";

      out.println(docType +
         "<html>\n" +
         "<head><title>" + title + "</title></head>\n"+
         "<body bgcolor = \"#f0f0f0\">\n" +
         "<h1 align = \"center\">" + title + "</h1>\n" +
         "<p>Current Time is: " + CT + "</p>\n"
      );
   }

   // Method to handle POST method request.
   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 [role="bare"] [role="bare"]http://localhost:8080/Refresh 调用此 servlet,它将在每 5 秒后显示当前系统时间,如下所示。只需运行 servlet 并等待查看结果 -

Auto Page Refresh using Servlet
Current Time is: 9:44:50 PM

Servlets - Sending Email

使用 Servlet 发送电子邮件非常简单,但前提是您应该在计算机上安装 JavaMail APIJava Activation Framework (JAF)

  1. 你可以从 Java 标准网站下载 JavaMail (Version 1.2) 的最新版本。

  2. 你可以从 Java 标准网站下载 JAF (Version 1.1.1) 的最新版本。

下载并解压缩这些文件,在新建的顶层目录中,你可以找到这两个应用程序的多个 jar 文件。你需要在你的 CLASSPATH 中添加 mail.jaractivation.jar 文件。

Send a Simple Email

以下示例演示从您的计算机发送一封简单的电子邮件。这里假设您的 localhost 已连接到互联网并能够发送电子邮件。同时,确保 CLASSPATH 中存在 Java Email API 包和 JAF 包中的所有 jar 文件。

// 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 {

      // Recipient's email ID needs to be mentioned.
      String to = "abcd@gmail.com";

      // Sender's email ID needs to be mentioned
      String from = "web@gmail.com";

      // Assuming you are sending email from localhost
      String host = "localhost";

      // Get system properties
      Properties properties = System.getProperties();

      // Setup mail server
      properties.setProperty("mail.smtp.host", host);

      // Get the default Session object.
      Session session = Session.getDefaultInstance(properties);

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      try {
         // Create a default MimeMessage object.
         MimeMessage message = new MimeMessage(session);

         // Set From: header field of the header.
         message.setFrom(new InternetAddress(from));

         // Set To: header field of the header.
         message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));

         // Set Subject: header field
         message.setSubject("This is the Subject Line!");

         // Now set the actual message
         message.setText("This is actual message");

         // Send 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\">\n";

         out.println(docType +
            "<html>\n" +
               "<head><title>" + title + "</title></head>\n" +
               "<body bgcolor = \"#f0f0f0\">\n" +
                  "<h1 align = \"center\">" + title + "</h1>\n" +
                  "<p align = \"center\">" + res + "</p>\n" +
               "</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 [role="bare"] [role="bare"]http://localhost:8080/SendEmail 调用此 servlet,它将向给定的电子邮件 ID abcd@gmail.com 发送一封电子邮件,并会显示以下响应 −

Send Email
Sent message successfully....

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

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

以下是参数说明 −

  1. type − 这将设置为 TO、CC 或 BCC。这里的 CC 表示抄送,BCC 表示密件抄送。示例 Message.RecipientType.TO

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

Send an HTML Email

以下示例演示从您的计算机发送一封 HTML 电子邮件。这里假设您的 localhost 已连接到互联网并能够发送电子邮件。同时,请确保 CLASSPATH 中存在 Java Email API 包和 JAF 包中的所有 jar 文件。

这个示例与之前的示例非常类似,只不过这里我们使用 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 {

      // Recipient's email ID needs to be mentioned.
      String to = "abcd@gmail.com";

      // Sender's email ID needs to be mentioned
      String from = "web@gmail.com";

      // Assuming you are sending email from localhost
      String host = "localhost";

      // Get system properties
      Properties properties = System.getProperties();

      // Setup mail server
      properties.setProperty("mail.smtp.host", host);

      // Get the default Session object.
      Session session = Session.getDefaultInstance(properties);

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      try {

         // Create a default MimeMessage object.
         MimeMessage message = new MimeMessage(session);

         // Set From: header field of the header.
         message.setFrom(new InternetAddress(from));

         // Set To: header field of the header.
         message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
         // Set Subject: header field
         message.setSubject("This is the Subject Line!");

         // Send the actual HTML message, as big as you like
         message.setContent("<h1>This is actual message</h1>", "text/html" );

         // Send 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\">\n";

         out.println(docType +
            "<html>\n" +
               "<head><title>" + title + "</title></head>\n" +
               "<body bgcolor = \"#f0f0f0\">\n" +
                  "<h1 align = \"center\">" + title + "</h1>\n" +
                  "<p align = \"center\">" + res + "</p>\n" +
               "</body>
            </html>"
         );
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
}

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

Send Attachment in Email

以下示例演示从您的计算机发送带有附件的电子邮件。这里假设您的 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 {

      // Recipient's email ID needs to be mentioned.
      String to = "abcd@gmail.com";

      // Sender's email ID needs to be mentioned
      String from = "web@gmail.com";

      // Assuming you are sending email from localhost
      String host = "localhost";

      // Get system properties
      Properties properties = System.getProperties();

      // Setup mail server
      properties.setProperty("mail.smtp.host", host);

      // Get the default Session object.
      Session session = Session.getDefaultInstance(properties);

	  // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      try {
         // Create a default MimeMessage object.
         MimeMessage message = new MimeMessage(session);

         // Set From: header field of the header.
         message.setFrom(new InternetAddress(from));

         // Set To: header field of the header.
         message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));

         // Set Subject: header field
         message.setSubject("This is the Subject Line!");

         // Create the message part
         BodyPart messageBodyPart = new MimeBodyPart();

         // Fill the message
         messageBodyPart.setText("This is message body");

         // Create a multipar message
         Multipart multipart = new MimeMultipart();

         // Set text message part
         multipart.addBodyPart(messageBodyPart);

         // Part two is attachment
         messageBodyPart = new MimeBodyPart();
         String filename = "file.txt";
         DataSource source = new FileDataSource(filename);
         messageBodyPart.setDataHandler(new DataHandler(source));
         messageBodyPart.setFileName(filename);
         multipart.addBodyPart(messageBodyPart);

         // Send the complete message parts
         message.setContent(multipart );

         // Send 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\">\n";

         out.println(docType +
            "<html>\n" +
               "<head><title>" + title + "</title></head>\n" +
               "<body bgcolor = \"#f0f0f0\">\n" +
                  "<h1 align = \"center\">" + title + "</h1>\n" +
                  "<p align = \"center\">" + res + "</p>\n" +
               "</body>
            </html>"
         );
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
}

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

User Authentication Part

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

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

其余的电子邮件发送机制如下所述。

Servlets - Packaging

涉及 WEB-INF 子目录的 Web 应用程序结构对于所有 Java Web 应用程序都是标准的,并由 servlet API 规范指定。给定顶层目录名称为 myapp。这个目录结构看起来像这样 -

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

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

Creating Servlets in Packages

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

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

以下示例使用包名为 com.myorg 创建 MyServlet 类

// Name your package
package com.myorg;

// Import required java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class MyServlet extends HttpServlet {

   private String message;

   public void init() throws ServletException {
      // Do required initialization
      message = "Hello World";
   }

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

      // Set response content type
      response.setContentType("text/html");

      // Actual logic goes here.
      PrintWriter out = response.getWriter();
      out.println("<h1>" + message + "</h1>");
   }

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

Compiling Servlets in Packages

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

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

$ javac MyServlet.java

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

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

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

Packaged Servlet Deployment

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

如果您有 com.myorg.MyServlet 的完全限定类名,那么此 servlet 类必须位于 WEB-INF/classes/com/myorg/MyServlet.class 中,并且您需要在位于 <Tomcat 安装目录>/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-app>…​</web-app> web.xml 文件内。此表格中可能已经存在各种条目,但不必担心。

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

Hello World

Servlets - Debugging

测试/调试 servlet 总是一件困难的事情。servlet 往往涉及大量客户端/服务器交互,因此错误可能很频繁,但难以重现。

以下是一些技巧和建议,可以帮助您进行调试。

System.out.println()

System.out.println() 易于用作标记,以测试程序的某个部分是否正在执行。我们还可以打印出变量值。此外 −

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

  2. 断点技术会停止正常执行,因此会花费更多的时间。而写入 System.out 不会过多地干扰应用程序的正常执行流程,这在时间至关重要时非常有价值。

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

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

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

Message Logging

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

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

// Import required java libraries
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");

      //Call the two ServletContext.log methods
      ServletContext context = getServletContext( );

      if (par == null || par.equals(""))
         //log version with Throwable parameter
         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\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + title + "</h1>\n" +
               "<h2 align = \"center\">Messages sent</h2>\n" +
            "</body>
         </html>"
      );
   } //doGet
}

ServletContext 将其文本消息记录到 servlet 容器的日志文件中。在 Tomcat 中,这些日志位于 <Tomcat 安装目录>/logs。

日志文件确实提示了新出现的错误或问题的发生频率。因此,在通常不应该发生的异常的 catch 子句中使用 log() 函数效果不错。

Using JDB Debugger

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

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

大多数调试器通过自动了解如何调试小程序隐藏此详细信息。直到它们对 servlet 执行相同的操作,您才必须通过执行以下操作帮助您的调试器 −

  1. 设置调试器的类路径,以便它可以查找 sun.servlet.http.Http-Server 及其关联类。

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

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

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

Using Comments

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

Servlet 使用 Java 注释,并且可以使用单行(// …​)和多行(/* …​ */)注释来临时删除 Java 代码的部分。如果错误消失了,请仔细查看您刚刚注释的代码并找出问题。

Client and Server Headers

有时,当 servlet 未按预期运行时,查看原始 HTTP 请求和响应非常有用。如果您熟悉 HTTP 的结构,您可以读取请求和响应,并确切地了解那些标头正在做什么。

Important Debugging Tips

此处列出了一些有关 servlet 调试的其他调试提示 −

  1. 请记住,server_root/classes 不重新加载,而 server_root/servlet 可能重新加载。

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

  3. 通过强制重新加载整个页面,确保浏览器不会缓存前一个请求的输出。使用 Netscape Navigator 时,使用 Shift-Reload;使用 Internet Explorer 时,使用 Shift-Refresh。

  4. 验证 servlet 的 init() 方法是否获取 ServletConfig 参数并立即调用 super.init(config)。

Servlets - Internationalization

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

  1. Internationalization (i18n) −这意味着使网站能够提供翻译成访问者语言或国籍的不同版本的网站内容

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

  3. locale −这是一个特定的文化或地理区域。它通常称为语言符号,后面跟一个国家符号,用下划线分隔。例如,“en_US”代表美国的英语语言环境。

在构建全球网站时,有许多事项需要照顾到。本教程不会为您提供这方面的完整详细信息,但会为您提供一个很好的示例,说明如何向互联网社区以不同的语言提供您的网页,方法是区分他们的位置,即语言环境。

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

java.util.Locale request.getLocale()

Detecting Locale

以下是您可以用来检测请求者的位置、语言和语言环境的重要语言环境方法。以下所有方法都显示了请求者的浏览器中设置的国家名称和语言名称。

Sr.No.

Method & Description

1

String getCountry() 此方法返回该区域设置的大写国家/地区代码,其 ISO 3166 格式为 2 个字母。

2

String getDisplayCountry() 此方法返回该区域设置的国家/地区名称,适合向用户显示。

3

String getLanguage() 此方法返回该区域设置的语言代码,其 ISO 639 格式为小写。

4

String getDisplayLanguage() 此方法返回该区域设置的语言名称,适合向用户显示。

5

String getISO3Country() 此方法返回该区域设置的国家/地区三个字母缩写。

6

String getISO3Language() 此方法返回该区域设置的语言三个字母缩写。

Example

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

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 {

      //Get the client's Locale
      Locale locale = request.getLocale();
      String language = locale.getLanguage();
      String country = locale.getCountry();

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

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

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + language + "</h1>\n" +
               "<h2 align = \"center\">" + country + "</h2>\n" +
         "</body>
         </html>"
      );
   }
}

Languages Setting

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

第二点是使用 HTML 实体显示所有特殊字符,例如,“ñ”代表“ñ”,而“¡”代表“¡”,如下所示:

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 {

      // Set response content type
      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\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1>" + "En Espa&ntilde;ol:" + "</h1>\n" +
               "<h1>" + "&iexcl;Hola Mundo!" + "</h1>\n" +
            "</body>
         </html>"
      );
   }
}

Locale Specific Dates

可使用 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 {

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      //Get the client's Locale
      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\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + date + "</h1>\n" +
            "</body>
         </html>"
      );
   }
}

Locale Specific Currency

可使用 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 {

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      //Get the client's Locale
      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\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + formattedCurr + "</h1>\n" +
            "</body>
         </html>"
      );
   }
}

Locale Specific Percentage

可使用 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 {

      // Set response content type
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      //Get the client's Locale
      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\">\n";

      out.println(docType +
         "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor = \"#f0f0f0\">\n" +
               "<h1 align = \"center\">" + formattedPerc + "</h1>\n" +
            "</body>
         </html>"
      );
   }
}

Servlets - Annotations

到目前为止,您已了解 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.

Annotation & Description

1

@WebServlet To declare a servlet.

2

@WebInitParam 指定初始化参数。

3

@WebFilter 声明 Servlet 过滤器。

4

@WebListener To declare a WebListener

5

*@HandlesTypes *声明 ServletContainerInitializer 可处理的类类型。

6

@HttpConstraint 此注释用在 ServletSecurity 注释中,用于表示对所有未在 ServletSecurity 注释中与相应的 HttpMethodConstraint 元素一起出现的 HTTP 协议方法应用的安全约束。

7

*@HttpMethodConstraint *该注解用于ServletSecurity注解中,以表示对特定HTTP协议消息的安全约束。

8

*@MultipartConfig *可指定在Servlet类中以指示Servlet的实例期望符合multipart/form-data MIME类型的请求的注解。

9

@ServletSecurity 该注解用于Servlet实现类中以指定Servlet容器在HTTP协议消息中强制执行的安全约束。

此处我们已详细讨论了一些注解。

@WebServlet

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

Sr.No.

Attribute & Description

1

String name Servlet的名称

2

String[] value URL模式数组

3

String[] urlPatterns 应用此Filter的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 属性。

Example

以下示例介绍如何使用@WebServlet注解。它是一个简单的servlet,用于显示文本 Hello 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。

现在只需运行 [role="bare"] [role="bare"]http://localhost:8080/Simple 即可尝试调用任何 servlet。您将在网页上看到以下输出。

Hello servlet

@WebInitParam

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

Sr.No.

Attribute & Description

1

String name 初始化参数的名称

2

String value 初始化参数的值

3

String description 初始化参数的描述

Example

以下示例介绍如何将 @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 中。

现在只需运行 [role="bare"] [role="bare"]http://localhost:8080/Simple 即可尝试调用任何 servlet。您将在网页上看到以下输出。

Hello Servlet

Hello World!

@Webfilter

这是用于声明 servlet 过滤器的注释。它在部署时由容器处理,并向指定的 URL 模式、servlet 和分派器类型应用相应的过滤器。

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

Sr.No.

Attribute & Description

1

String filterName 过滤器的名称

2

* 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 此过滤器的图标(如果存在)

Example

以下示例描述了如何使用 @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.*;

// Implements Filter class

@WebFilter(urlPatterns = {"/*"}, initParams = {
   @WebInitParam(name = "test-param", value = "Initialization Paramter")})
public class LogFilter implements Filter {

   public void init(FilterConfig config) throws ServletException {
      // Get init parameter
      String testParam = config.getInitParameter("test-param");

      //Print the init parameter
      System.out.println("Test Param: " + testParam);
   }

   public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException {

      // Log the current timestamp.
      System.out.println("Time " + new Date().toString());

      // Pass request back down the filter chain
      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。

现在只需运行 [role="bare"] [role="bare"]http://localhost:8080/Simple 即可尝试调用任何 servlet。您将在网页上看到以下输出。

Hello Servlet

Hello World!

现在,打开 servlet 控制台。在那里,您将看到 init 参数 testparamcurrent timestamp 的值以及 servlet 通知消息。