- Servlet 教程
- Servlet - 主页
- Servlet - 概览
- Servlet - 环境设置
- Servlet - 生命周期
- Servlet - 实例
- Servlet - 表单数据
- Servlet - 客户端请求
- Servlet - 服务器响应
- Servlet - Http 代码
- Servlet - 编写过滤器
- Servlet - 异常
- Servlet - Cookie 处理
- Servlet - 会话跟踪
- Servlet - 数据库访问
- Servlet - 文件上载
- Servlet - 处理日期
- Servlet - 页面重定向
- Servlet - 访问计数器
- Servlet - 自动刷新
- Servlet - 发送电子邮件
- Servlet - 打包
- Servlet - 调试
- Servlet - 国际化
- Servlet - 标注
- Servlet 实用资源
- Servlet - 问题与解答
- Servlet - 速查指南
- Servlet - 实用资源
- Servlet - 讨论
Servlet - 速查指南
Servlet - 概览
什么是 Servlet?
Java Servlet 是在 Web 或应用程序服务器上运行的程序,充当来自 Web 浏览器或其他 HTTP 客户端的请求与 HTTP 服务器上的数据库或应用程序之间的中间层。
使用 Servlet,您可以通过网页表单收集用户输入,显示来自数据库或其他来源的记录并动态创建网页。
Java Servlet 通常与使用通用网关接口 (CGI) 实现的程序具有相同用途。但是,与 CGI 相比,Servlet 具有多项优势。
性能大幅提升。
Servlet 在 Web 服务器的地址空间内执行。不必为处理每个客户端请求都创建单独的进程。
Servlet 由于是用 Java 编写的,因此与平台无关。
服务器上的 Java 安全管理器强制执行一组限制以保护服务器计算机上的资源。因此,Servlet 是可信的。
Servlet 可以使用 Java 类库的全部功能。它可以通过已经了解的套接字和 RMI 机制与 applet、数据库或其他软件进行通信。
Servlet 架构
下图显示了 Servlet 在 Web 应用程序中的位置。
Servlet 任务
Servlet 执行下列主要任务 −
读取客户端(浏览器)发送的显式数据。这包括网页上的 HTML 表单,也可以来自 applet 或自定义 HTTP 客户端程序。
读取客户端(浏览器)发送的隐式 HTTP 请求数据。这包括 Cookie、浏览器理解的媒体类型和压缩方案等。
处理数据并生成结果。此过程可能需要与数据库通信、执行 RMI 或 CORBA 调用、调用 Web 服务或直接计算响应。
向客户端(浏览器)发送显式数据(即文档)。此文档可以多种格式发送,包括文本(HTML 或 XML)、二进制(GIF 图像)、Excel 等。
向客户端(浏览器)发送隐式 HTTP 响应。这包括告知浏览器或其他客户端正在返回何种类型的文档(例如 HTML)、设置 Cookie 和缓存参数以及其他此类任务。
Servlet 软件包
Servlet 是 Java 类,由具有支持 Java Servlet 规范的解释器的 Web 服务器运行。
使用javax.servlet和javax.servlet.http包可以创建 Servlet,它们是 Java 企业版的标准部分,后者是支持大规模开发项目的 Java 类库的扩展版本。
这些类实现 Java Servlet 和 JSP 规范。在编写本教程时,Java Servlet 为 2.5 版,JSP 为 2.1 版。
Java servlet 的创建和编译与任何其他 Java 类一样。安装 servlet 包并将它们添加到计算机的类路径后,可以使用 JDK 的 Java 编译器或任何其他当前编译器编译 servlet。
接下来是什么?
我将一步步带你设置环境,开始学习 Servlet。因此,系好安全带,体验 servlet 之旅。我确信你会非常喜欢本教程。
Servlet - 环境设置
开发环境是你开发、测试并最终运行 Servlet 的环境。
与任何其他 Java 程序一样,你需要使用 Java 编译器javac编译 servlet,在编译 servlet 应用程序后,将其部署在配置好的环境中以进行测试和运行。
此开发环境设置涉及以下步骤 -
设置 Java 开发工具包
此步骤涉及下载 Java 软件开发工具包 (SDK) 的实现并相应设置 PATH 环境变量。
你可以从 Oracle 的 Java 站点下载 SDK - Java SE 下载。
下载 Java 实现后,按照给定的说明进行安装和配置设置。最后设置 PATH 和 JAVA_HOME 环境变量,使其分别引用包含 java 和 javac 的目录,通常为 java_install_dir/bin 和 java_install_dir。
如果你运行的是 Windows,并将 SDK 安装在 C:\jdk1.8.0_65 中,则可以在 C:\autoexec.bat 文件中添加以下行。
set PATH = C:\jdk1.8.0_65\bin;%PATH% set JAVA_HOME = C:\jdk1.8.0_65
或者,在 Windows NT/2000/XP 中,你还可以在“我的电脑”上单击鼠标右键,选择“属性”,然后选择“高级”和“环境变量”。然后,你需要更新 PATH 值并按“确定”按钮。
在 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
或者,如果你使用 Borland JBuilder、Eclipse、IntelliJ IDEA 或 Sun ONE Studio 等集成开发环境 (IDE),请编译并运行一个简单程序以确认 IDE 知道你安装 Java 的位置。
设置 Web 服务器 - Tomcat
市场上有很多支持 servlet 的 Web 服务器。有些 Web 服务器可以免费下载,而 Tomcat 就是其中之一。
Apache Tomcat 是 Java Servlet 和 Java Server Pages 技术的开源软件实现,可以使用它作为单独的服务器来测试 servlet,也可以将其与 Apache Web 服务器集成。以下是设置你的计算机上 Tomcat 的步骤 -
从 https://tomcat.net.cn/ 下载最新版本的 Tomcat。
下载安装程序后,将二进制发行版解压缩到方便的位置。例如,在 Windows 中的 C:\apache-tomcat-8.0.28,或者在 Linux/Unix 中的 /usr/local/apache-tomcat-8.0.289,并创建指向这些位置的 CATALINA_HOME 环境变量。
通过在 Windows 计算机上执行以下命令可以启动 Tomcat -
%CATALINA_HOME%\bin\startup.bat 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
启动后,可以通过访问https://127.0.0.1:8080/来使用 Tomcat 附带的默认 Web 应用程序。如果一切正常,则应显示以下结果 -
有关配置和运行 Tomcat 的更多信息,请参阅此处包含的文档以及 Tomcat 网站 - https://tomcat.net.cn
Tomcat 可通过在 Windows 电脑上执行以下命令停止 −
C:\apache-tomcat-8.0.28\bin\shutdown
Tomcat 可通过在 Unix(Solaris、Linux 等)电脑上执行以下命令停止 −
/usr/local/apache-tomcat-8.0.28/bin/shutdown.sh
设置 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
注 − 假设你的开发目录为 C:\ServletDevel(Windows)或 /usr/ServletDevel(Unix),那么你需要在 CLASSPATH 中添加这些目录,方式与你在上面添加的一样。
Servlet - 生命周期
servlet 生命周期可以定义为从其创建到销毁的整个过程。以下是 servlet 遵循的路径。
servlet 通过调用 init() 方法初始化。
servlet 调用 service() 方法处理来自客户端的请求。
servlet 通过调用 destroy() 方法终止。
最后,servlet 由 JVM 的垃圾回收器进行垃圾回收。
现在让我们详细讨论一下生命周期方法。
init() 方法
init 方法只调用一次。它只在 servlet 创建时调用,之后不对任何用户请求调用。所以它用于一次性初始化,就像 applet 的 init 方法一样。
当用户第一次调用对应于 servlet 的 URL 时,servlet 通常会被创建,但你也可以指定在服务器第一次启动时加载 servlet。
当用户调用 servlet 时,每个 servlet 的单个实例都会被创建,每个用户请求都会产生一个新线程,交由 doGet 或 doPost(视情况而定)。init() 方法只会创建或加载一些将在 servlet 整个生命周期中使用的数据。
init 方法的定义如下 −
public void init() throws ServletException { // Initialization code... }
service() 方法
service() 方法是执行实际任务的主要方法。servlet 容器(即网络服务器)调用 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 请求中使用最频繁的方法。以下是这两个方法的签名。
doGet() 方法
GET 请求源自对 URL 的正常请求或未指定 METHOD 的 HTML 表单,应由 doGet() 方法处理。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Servlet code }
doPost() 方法
POST 请求源自明确将 POST 列为 METHOD 的 HTML 表单,应由 doPost() 方法处理。
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Servlet code }
destroy() 方法
destroy() 方法只在 servlet 生命周期结束时调用一次。此方法为你的 servlet 提供关闭数据库连接、停止后台线程、将 cookie 列表或点击计数写入磁盘以及执行其他此类清理活动的机会。
在调用 destroy() 方法后,servlet 对象被标记为垃圾回收。destroy 方法的定义如下 −
public void destroy() { // Finalization code... }
架构图
下图描述了典型的 Servlet 生命周期场景。
首先,发往服务器的 HTTP 请求会被委托给 Servlet 容器。
在调用 service() 方法之前,Servlet 容器会加载 Servlet。
然后,Servlet 容器通过衍生多个线程(每个线程执行 Servlet 的单个实例的 service() 方法)来处理多个请求。
Servlet - 实例
Servlet 是 Java 类,可服务于 HTTP 请求并实现 **javax.servlet.Servlet** 接口。Web 应用程序开发人员通常编写的是扩展自 **javax.servlet.http.HttpServlet** 的 Servlet,javax.servlet.http.HttpServlet 是一个实现 Servlet 接口,且经过专门设计,用于处理 HTTP 请求的抽象类。
示例代码
以下是显示 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. } }
编译 Servlet
让我们创建一个名为 HelloWorld.java 的文件,其中包含上面所示的代码。将此文件放置在 C:\ServletDevel(在 Windows 中)或 /usr/ServletDevel(在 Unix 中)。在继续进行之前,必须将此路径位置添加到 CLASSPATH。
假设您的环境设置正确,请转到 **ServletDevel** 目录并按照以下指令编译 HelloWorld.java −
$ javac HelloWorld.java
如果 Servlet 依赖于任何其他库,您还必须将这些 JAR 文件包含在您的 CLASSPATH 中。我仅包含了 servlet-api.jar JAR 文件,因为我在 Hello World 程序中不使用任何其他库。
此命令行使用 Sun Microsystems Java 软件开发包 (JDK) 附带的内置 javac 编译器。为了使此命令正常工作,您必须在 PATH 环境变量中包含您正在使用的 Java SDK 的位置。
如果一切顺利,上面的编译将在同一目录中生成 **HelloWorld.class** 类文件。下一部分将说明如何将已编译的 Servlet 部署到生产环境中。
Servlet 部署
默认情况下,Servlet 应用程序位于路径 <Tomcat-installationdirectory>/webapps/ROOT,类文件将驻留在 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中。
如果您拥有 **com.myorg.MyServlet** 的完全限定类名,则此 servlet 类必须位于 WEB-INF/classes/com/myorg/MyServlet.class 中。
现在,让我们将 HelloWorld.class 复制到 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes,并在位于 <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.xml 文件中可用的 <web-app>...</web-app> 标记内部的上述条目。此表中可能已提供各种条目,但不必理会。
您几乎已完成,现在让我们使用 <Tomcat-installationdirectory>\bin\startup.bat(在 Windows 中)或 <Tomcat-installationdirectory>/bin/startup.sh(在 Linux/Solaris 等中)启动 tomcat 服务器,最后在浏览器地址框中键入 **https://127.0.0.1:8080/HelloWorld**。如果一切顺利,您将获得以下结果
Servlet - 表单数据
您一定遇到过需要将某些信息从您的浏览器传递到网络服务器并最终传递到您的后端程序的情况。浏览器使用两种方法将此信息传递到网络服务器。这些方法是 GET 方法和 POST 方法。
GET 方法
GET 方法将编码的用户信息附加在页面请求后面发送。页面和编码信息由 **?**(问号)符号分隔,如下所示 −
http://www.test.com/hello?key1 = value1&key2 = value2
GET 方法是将信息从浏览器传递到 Web 服务器的默认方法,它会产生一个很长的字符串,此字符串会出现在浏览器的位置:框中。如果您有密码或其他敏感信息要传递到服务器,则切勿使用 GET 方法。GET 方法有大小限制:请求字符串中只能使用 1024 个字符。
此信息使用 QUERY_STRING 标头传递,并且可以通过 QUERY_STRING 环境变量访问,而 Servlet 使用 doGet() 方法处理此类请求。
POST 方法
将信息传递到后端程序时,通常更可靠的方法是 POST 方法。这种方法打包信息的方式与 GET 方法完全相同,但它不是在 URL 中的 ?(问号)后面将其作为文本字符串发送,而是将其作为单独的消息发送。此消息以标准输入的形式发送到后端程序,您可以对其进行解析并将其用于处理。Servlet 使用 doPost() 方法处理此类请求。
使用 Servlet 读取表单数据
根据情况,Servlet 使用以下方法自动处理表单数据解析 −
getParameter() − 调用 request.getParameter() 方法以获取表单参数的值。
getParameterValues() − 如果参数出现多次,则调用此方法并返回多个值(例如选择框)。
getParameterNames() − 如果您想要当前请求中所有参数的完整列表,请调用此方法。
使用 URL 的 GET 方法示例
下面是一个简单的 URL,它将使用 GET 方法向 HelloForm 程序传递两个值。
https://127.0.0.1:8080/HelloForm?first_name = ZARA&last_name = ALI下面是处理 Web 浏览器提供输入的 HelloForm.java Servlet 程序。我们将使用 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 安装目录>/webapps/ROOT/WEB-INF/classes 中,并在 <Tomcat 安装目录>/webapps/ROOT/WEB-INF/ 中找到的 web.xml 文件中创建以下条目:
<servlet> <servlet-name>HelloForm</servlet-name> <servlet-class>HelloForm</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloForm</servlet-name> <url-pattern>/HelloForm</url-pattern> </servlet-mapping>
现在在浏览器的 Location:box 中键入 https://127.0.0.1:8080/HelloForm?first_name=ZARA&last_name=ALI,并且在浏览器中执行上述命令之前,请确保您已经启动了 Tomcat 服务器。这将生成以下结果 −
Using GET Method to Read Form Data
- First Name: ZARA
- Last Name: ALI
使用表单的 GET 方法示例
下面是一个简单的示例,它使用 HTML FORM 和提交按钮传递两个值。我们将使用相同的 HelloForm 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 目录中。当您访问 https://127.0.0.1:8080/Hello.htm 时,以下是上述表单的实际输出。
尝试输入名字和姓氏,然后单击提交按钮,在本地计算机上查看结果(运行 Tomcat 的计算机)。根据提供的输入,它将生成与上述示例中提到的类似结果。
使用表单的 POST 方法示例
让我们对上述 Servlet 进行一些修改,以便它可以处理 GET 和 POST 方法。下面是使用 GET 或 POST 方法处理 Web 浏览器提供的输入的 HelloForm.java Servlet 程序。
// 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,然后使用以下 POST 方法和 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 的计算机)。
根据提供的输入,它将生成与上述示例中提到的类似结果。
向 Servlet 程序传递选择框数据
如果需要选择多个选项,则使用选择框。
下面是具有两个选择框的表单的 HTML 代码示例 CheckBox.htm
<html> <body> <form action = "CheckBox" method = "POST" target = "_blank"> <input type = "checkbox" name = "maths" checked = "checked" /> Maths <input type = "checkbox" name = "physics" /> Physics <input type = "checkbox" name = "chemistry" checked = "checked" /> Chemistry <input type = "submit" value = "Select Subject" /> </form> </body> </html>
以下代码会生成以下形式的结果
以下是用于处置 Web 浏览器中为复选框按钮提供的输入的 CheckBox.java servlet 程序。
// 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
读取全部表单参数
以下是使用 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 以读取任何其他包含其他对象(如文本框、单选按钮或下拉菜单等)的数据的表单。
Servlet - 客户机 HTTP 请求
当浏览器请求一个网页时,它将发送大量信息给 Web 服务器,这些信息无法直接读取,因为这些信息的传输是 HTTP 请求报头的一部分。你可以查看 HTTP 协议 以了解更多相关信息。
以下是从浏览器端获取的重要头部信息,并且在 Web 编程中非常常用 −
编号 | 头部和描述 |
---|---|
1 | Accept 此头部指定浏览器或其他客户机可以处理的 MIME 类型。image/png 或 image/jpeg 的值为两个最常见的可能性。 |
2 | Accept-Charset 此头部指定浏览器可以用于显示信息的字符集。例如,ISO-8859-1。 |
3 | Accept-Encoding 此头部指定浏览器知道如何处理的编码的类型。gzip 或 compress 的值为两个最常见的可能性。 |
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 此头部指示只有在指定日期后页面有所更改时客户机才需要该页面。如果未获得较新的结果,则服务器会发送 code,304 表示 未修改 的头部。 |
11 | If-Unmodified-Since 此头部是 If-Modified-Since 的反向,它指定的操作仅在文档早于指定日期时才应成功。 |
12 | Referer 此头部指示引用网页的 URL。例如,如果你在网页 1 上并点击指向网页 2 的链接时,当浏览器请求网页 2 时,网页 1 的 URL 将包含在 Referer 头部中。 |
13 | User-Agent 此头部标识发起请求的浏览器或其他客户机,并可用于将不同的内容返还给不同类型的浏览器。 |
读取 HTTP 头部的使用方法
可在 servlet 程序中使用以下方法来读取 HTTP 头部。这些方法可通过 HttpServletRequest 对象获得
编号 | 方法和描述 |
---|---|
1 | Cookie[] getCookies() 返回客户端随此请求发送的所有 Cookie 对象包含的数组。 |
2 | 枚举 getAttributeNames() 返回一个枚举,内含此请求可用的属性名称。 |
3 | 枚举 getHeaderNames() 返回此请求包含的所有头名称的枚举。 |
4 | 枚举 getParameterNames() 返回 String 对象的枚举,内含此请求中包含的参数的名称。 |
5 | HttpSession getSession() 返回与此请求关联的当前会话,或在请求没有会话时,创建一个会话。 |
6 | HttpSession getSession(boolean create) 返回与此请求关联的当前 HttpSession 或在没有当前会话且 create 的值为 true 时,返回一个新会话。 |
7 | Locale getLocale() 根据 Accept-Language 头,返回客户端将接受何种首选语言的内容。 |
8 | Object getAttribute(String name) 返回命名属性的值,如果给定名称的属性不存在,则返回 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() 返回发送请求客户端的互联网协议 (IP) 地址。 |
21 | String getRemoteHost() 返回发送请求客户端的完全限定名。 |
22 | String getRemoteUser() 如果用户已通过身份验证,则返回提出此请求的用户的登录,或在用户未通过身份验证时返回 null。 |
23 | String getRequestURI() 返回此请求的 URL 中从协议名称到 HTTP 请求第一行中查询字符串的部分。 |
24 | String getRequestedSessionId() 返回客户端指定的会话 ID。 |
25 | String getServletPath() 返回此请求的 URL 中调用 JSP 的部分。 |
26 | String[] getParameterValues(String name) 返回 String 对象的数组,内含给定请求参数所有的值,或在参数不存在时返回 null。 |
27 | boolean isSecure() 返回一个布尔值,指示此请求是否通过安全隧道(例如 HTTPS)发出。 |
28 | int getContentLength() 返回请求主体的长度(以字节为单位),由输入流提供,或在长度未知时返回 -1。 |
29 | int getIntHeader(String name) 以 int 的形式返回指定请求头的值。 |
30 | int getServerPort() 返回接收此请求的端口号。 |
HTTP 标头请求示例
下面是一个示例,它使用 getHeaderNames() 方法从 HttpServletRequest 中读取 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
Servlet - 服务器 HTTP 响应
如前一章所述,当 Web 服务器响应 HTTP 请求时,响应通常由状态行、一些响应标头、空行和文档组成。典型的响应如下所示 -
HTTP/1.1 200 OK Content-Type: text/html Header2: ... ... HeaderN: ... (Blank Line) <!doctype ...> <html> <head>...</head> <body> ... </body> </html>
该状态行包含 HTTP 版本(示例中为 HTTP/1.1)、一个状态代码(示例中为 200)和与状态代码(示例中为 OK)对应的一个很短的消息。
以下是 Web 服务器最常用的 HTTP 1.1 响应标头,您将在 Web 编程中使用它们 -
编号 | 头部和描述 |
---|---|
1 | Allow 该标头指定服务器支持的请求方法 (GET、POST 等)。 |
2 | Cache-Control 该标头指定响应文档可以安全缓存的情况下。它的值可以为 public、private 或 nocache 等。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 该标头指示响应中的字节数。只有当浏览器使用持久(keep-alive)HTTP 连接时才需要该信息。 |
8 | Content-Type 该标头提供响应文档的 MIME(多用途互联网邮件扩展)类型。 |
9 | Expires 该标头指定内容应被视为过时的时间,因此不再缓存。 |
10 | Last-Modified 该标头指示文档最后一次更改的时间。然后,客户端可以缓存文档,并在以后的请求中通过 If-Modified-Since 请求标头提供日期。 |
11 | Location 该标头应包含在所有状态代码为 300 的响应中。这会将文档地址通知浏览器。浏览器会自动重新连接到该位置并检索新文档。 |
12 | Refresh 该标头指定浏览器多久请求更新的页面。您可以指定以秒为单位的时间,页面将在该时间后刷新。 |
13 | Retry-After 该标头可以与 503(服务不可用)响应结合使用,以告诉客户端多久可以重复其请求。 |
14 | Set-Cookie 该标头指定与页面关联的 cookie。 |
设置 HTTP 响应标头的方法
以下是可用于在您的 servlet 程序中设置 HTTP 响应标头的方法。这些方法可通过 HttpServletResponse 对象获得。
编号 | 方法和描述 |
---|---|
1 | String encodeRedirectURL(String url) 对指定的 URL 进行编码以用于 sendRedirect 方法,或如果不需要编码,则返回未更改的 URL。 |
2 | String encodeURL(String url) 对指定的 URL 进行编码,方法是包含其中的会话 ID,或如果不需要编码,则返回未更改的 URL。 |
3 | boolean containsHeader(String name) 返回布尔值,表明所命名的响应标头是否已被设置。 |
4 | boolean isCommitted() 返回布尔值,表明响应是否被提交。 |
5 | void addCookie(Cookie cookie) 将指定的 cookie 添加到响应中。 |
6 | void addDateHeader(String name, long date) 使用给定的名称和日期值添加响应标头。 |
7 | void addHeader(String name, String value) 使用给定的名称和值添加响应标头。 |
8 | void addIntHeader(String name, int value) 使用给定的名称和整数值添加响应标头。 |
9 | void flushBuffer() 强制缓冲区中的任何内容写到客户端。 |
10 | void reset() 清除缓存在任何数据,以及状态代码和标头。 |
11 | void resetBuffer() 清除响应中底层缓冲区的内容,而不清除标头或状态代码。 |
12 | void sendError(int sc) 使用指定的错误代码向客户端发送错误响应,并清除缓冲区。 |
13 | void sendError(int sc, String msg) 使用指定的错误向客户端发送错误响应 |
14 | void sendRedirect(String location) 使用指定的重定向位置 URL 向客户端发送临时重定向响应。 |
15 | void setBufferSize(int size) 设置响应正文的首选缓冲区大小。 |
16 | void setCharacterEncoding(String charset) 设置发送到客户端的响应的字符编码(MIME 字符集),例如 UTF-8。 |
17 | void setContentLength(int len) 设置响应中内容正文的长度。在 HTTP servlet 中,此方法设置 HTTP Content-Length 标头。 |
18 | void setContentType(String type) 设置发送到客户端的响应的内容类型(如果响应尚未提交)。 |
19 | void setDateHeader(String name, long date) 使用给定的名称和日期值设置响应标头。 |
20 | void setHeader(String name, String value) 使用给定的名称和值设置响应标头。 |
21 | void setIntHeader(String name, int value) 使用给定的名称和整数值设置响应标头 |
22 | void setLocale(Locale loc) 设置响应的语言环境(如果响应尚未提交)。 |
23 | void setStatus(int sc) 设置该响应的状态代码 |
HTTP 标头响应示例
已经在前面的示例中看到 setContentType() 方法的作用,下面的示例也将会使用同样的方法,此外还会使用 setIntHeader() 方法来设置 Refresh 标头。
// 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
Servlet - HTTP 状态代码
HTTP 请求和 HTTP 响应消息的格式相似,并且具有以下结构 -
一个初始状态行 + CRLF(回车 + 换行符,即换行)
零个或多个标头行 + CRLF
一个空行,即 CRLF
一个可选的消息正文,如文件、查询数据或查询输出。
例如,服务器响应标头如下所示 -
HTTP/1.1 200 OK Content-Type: text/html Header2: ... ... HeaderN: ... (Blank Line) <!doctype ...> <html> <head>...</head> <body> ... </body> </html>
该状态行包含 HTTP 版本(示例中为 HTTP/1.1)、一个状态代码(示例中为 200)和与状态代码(示例中为 OK)对应的一个很短的消息。
下面列出了从 Web 服务器返回的 HTTP 状态代码和相关消息 -
代码 | 消息 | 描述 |
---|---|---|
100 | 继续 | 服务器只接收到了请求的一部分,但只要它没有被拒绝,客户端就应该继续进行请求 |
101 | 切换协议 | 服务器切换协议。 |
200 | 确定 | 请求是正确的 |
201 | 已创建 | 请求完成,已创建新资源 |
202 | 已被接受 | 已接受处理请求,但处理尚未完成。 |
203 | 非授权信息 | |
204 | 无内容 | |
205 | 重置内容 | |
206 | 部分内容 | |
300 | 多项选择 | 链接列表。用户可以选择一个链接并转到该位置。最多五个地址 |
301 | 永久移动 | 请求的页面已移至新网址 |
302 | 已找到 | 请求的页面已暂时移至新网址 |
303 | 请查看其他内容 | 请求的页面可以在不同的网址下找到 |
304 | 未修改 | |
305 | 使用代理 | |
306 | 未使用 | 此代码曾在以前版本中使用过。不再使用,但代码已保留 |
307 | 临时重定向 | 请求的页面已暂时移动到新网址。 |
400 | 请求错误 | 服务器未能理解请求 |
401 | 未授权 | 请求的页面需要用户名和密码 |
402 | 需要付款 | 您目前无法使用此代码 |
403 | 禁止 | 禁止访问请求的页面 |
404 | 未找到 | 服务器找不到请求的页面。 |
405 | 方法不允许 | 请求中指定的方法不允许。 |
406 | 不可接受 | 服务器只能生成客户端无法接受的响应。 |
407 | 需要代理身份验证 | 在提供此请求之前,您必须使用代理服务器进行身份验证。 |
408 | 请求超时 | 请求花费的时间超出了服务器准备等待的时间。 |
409 | 冲突 | 由于冲突,请求无法完成。 |
410 | 已消失 | 请求的页面不再可用。 |
411 | 需要长度 | 未定义“Content-Length”。如果没有此项目,服务器将不接受请求。 |
412 | 前提失败 | 请求中给出的前提条件由服务器评估为假。 |
413 | 请求实体过大 | 服务器将不接受请求,因为请求实体过大。 |
414 | 请求 URL 过长 | 服务器将不接受请求,因为 URL 过长。当您将“post”请求转换为具有长查询信息的“get”请求时,会出现此情况。 |
415 | 不支持的媒体类型 | 服务器将不接受请求,因为媒体类型不受支持。 |
417 | 期望失败 | |
500 | 内部服务器错误 | 请求未完成。服务器遇到意外情况。 |
501 | 未实现 | 请求未完成。服务器不支持所需的功能。 |
502 | 错误网关 | 请求未完成。服务器从上游服务器收到无效响应。 |
503 | 服务不可用 | 请求未完成。服务器暂时过载或宕机。 |
504 | 网关超时 | 网关已超时。 |
505 | 不支持的 HTTP 版本 | 服务器不支持“http 协议”版本。 |
设置 HTTP 状态码的方法
可以在 Servlet 程序中使用以下方法设置 HTTP 状态码。这些方法可与HttpServletResponse对象一起使用。
编号 | 方法和描述 |
---|---|
1 | public void setStatus ( int statusCode ) 该方法设置一个任意的状态码。setStatus 方法采用一个整数(状态码)作为参数。如果您的响应包含特殊状态码和文档,请务必在使用PrintWriter返回任何内容之前调用 setStatus。 |
2 | public void sendRedirect(String url) 该方法生成一个 302 响应以及指定新文档网址的位置标头 |
3 | public void sendError(int code, String message) 该方法发送一个状态码(通常为 404)以及一个简短消息,该消息会自动格式化在 HTML 文档中并发送到客户端。 |
示例 HTTP 状态码
以下是发送 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
Servlet - 编写过滤器
Servlet 过滤器是可以用于 Servlet 编程的 Java 类,用于以下目的 −
在客户端访问后端资源之前拦截来自客户端的请求。
在将服务器上的响应发送回客户端之前对其进行处理。
规范中建议使用各种类型的过滤器 −
- 身份验证过滤器。
- 数据压缩过滤器。
- 加密过滤器。
- 触发资源访问事件的过滤器。
- 图像转换过滤器。
- 日志记录和审计过滤器。
- MIME 类型链过滤器。
- 令牌化过滤器。
- 转换 XML 内容的 XSLT 过滤器。
过滤器部署在部署描述符文件 web.xml 中,然后映射到应用程序的部署描述符中的 servlet 名称或 URL 模式。
当 Web 容器启动 Web 应用程序时,它会为在部署描述符中声明的每个过滤器创建一个实例。过滤器按其在部署描述符中声明的顺序执行。
Servlet 过滤器方法
过滤器只是实现 javax.servlet.Filter 接口的 Java 类。javax.servlet.Filter 接口定义了三个方法 −
编号 | 方法和描述 |
---|---|
1 | public void doFilter (ServletRequest, ServletResponse, FilterChain) 当由于客户端请求链末尾的资源而将请求/响应对通过该链传递时,容器每次都会调用此方法。 |
2 | public void init(FilterConfig filterConfig) Web 容器调用此方法向过滤器指示它正在投入服务。 |
3 | public void destroy() Web 容器调用此方法向过滤器指示它正在退出服务。 |
Servlet 过滤器 − 示例
以下是要打印客户端 IP 地址和当前日期时间的 Servlet 过滤器示例。此示例将让你对 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 安装目录>/webapps/ROOT/WEB-INF/classes 中
Web.xml 中的 Servlet 过滤器映射
定义过滤器并将其映射到 URL 或 Servlet,方式与定义 Servlet 并将其映射到 URL 模式大致相同。在部署描述符文件 web.xml 中为 filter 标记创建以下条目
<filter> <filter-name>LogFilter</filter-name> <filter-class>LogFilter</filter-class> <init-param> <param-name>test-param</param-name> <param-value>Initialization Paramter</param-value> </init-param> </filter> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
上述过滤器将适用于所有 servlet,因为我们在配置中指定了 /*。只有在想要对几个 servlet 应用过滤器时,才能指定特定的 servlet 路径。
现在尝试以通常的方式调用任何 servlet,你将在 Web 服务器日志中看到生成的日志。你可以使用 Log4J 记录器将上述日志记录到单独的文件中。
使用多个过滤器
Web 应用程序可以定义几个具有特定目的的不同过滤器。假设你定义了两个过滤器 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>
过滤器应用程序顺序
web.xml 中 filter-mapping 元素的顺序决定了 Web 容器将过滤器应用于 servlet 的顺序。要反转过滤器的顺序,只需反转 web.xml 文件中的 filter-mapping 元素顺序。
例如,上面的示例将首先应用 LogFilter,然后它将 AuthenFilter 应用于任何 servlet,但以下示例将反转此顺序 −
<filter-mapping> <filter-name>AuthenFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Servlet - 异常处理
当 Servlet 抛出异常时,Web 容器会搜索 web.xml 中与抛出的异常类型匹配的配置中的 exception-type 元素。
你必须使用 web.xml 中的 error-page 元素来指定对某些 异常 或 HTTP 状态代码 进行 Servlet 调用。
web.xml 配置
考虑一下,你有一个 ErrorHandler servlet,每当出现任何已定义的异常或错误时都会调用它。以下将在 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 Handler,那么你应该定义以下 error-page,而不是为每个异常定义单独的 error-page 元素 −
<error-page> <exception-type>java.lang.Throwable</exception-type > <location>/ErrorHandler</location> </error-page>
以下是关于用于异常处理的 web.xml 的注意事项 −
Servlet ErrorHandler 以与任何其他 servlet 相同的方式定义且在 web.xml 中配置。
如果状态代码出现任何错误,无论是 404(未找到)还是 403(禁止访问),ErrorHandler servlet 都会被调用。
如果 Web 应用程序抛出 ServletException 或 IOException,那么 Web 容器会调用 /ErrorHandler servlet。
你可以定义不同的 Error Handler 来处理不同类型的错误或异常。上面的例子非常通用,希望它起作用,向你解释了基本概念。
请求属性 − 错误/异常
以下是一组错误处理 servlet 可访问以分析错误/异常性质的请求属性。
编号 | 属性 & 说明 |
---|---|
1 | javax.servlet.error.status_code 此属性给出状态代码,可在存储到 java.lang.Integer 数据类型之后进行存储和分析。 |
2 | javax.servlet.error.exception_type 此属性提供有关异常类型的信息,可在存储到 java.lang.Class 数据类型之后进行存储和分析。 |
3 | javax.servlet.error.message 此属性提供确切的错误消息,可在存储到 java.lang.String 数据类型之后进行存储和分析。 |
4 | javax.servlet.error.request_uri 此属性提供有关调用 servlet 的 URL 的信息,可在存储到 java.lang.String 数据类型之后进行存储和分析。 |
5 | javax.servlet.error.exception 此属性提供有关引发异常的信息,其可进行存储和分析。 |
6 | javax.servlet.error.servlet_name 此属性提供 servlet 名称,可在存储到 java.lang.String 数据类型之后进行存储和分析。 |
错误处理程序 Servlet 示例
此示例将让你基本了解 Servlet 中的异常处理,但你可以使用同一概念编写更复杂的过滤器应用程序 −
// 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("https://127.0.0.1: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 并将类文件放在
让我们在 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>
现在,尝试使用引发任何异常的 servlet 或键入错误 URL,这将触发 Web 容器来调用 ErrorHandler servlet 并显示编程后的适当消息。例如,如果你键入错误的 URL,那么它将显示以下结果 −
The status code : 404
上述代码可能不适用于某些 Web 浏览器。因此请使用 Mozilla 和 Safari,它们应该起作用。
Servlet - Cookie 处理
Cookie 是存储在客户端电脑上的文本文件,用于各种信息跟踪目的。Java Servlet 透明地支持 HTTP cookie。
识别返回用户涉及三个步骤 −
服务器脚本发送一组 cookie 到浏览器。例如,姓名、年龄或身份证号等。
浏览器为将来使用而在本地机器上存储此信息。
当浏览器下次发送任何请求到 Web 服务器时,它会发送那些 cookie 信息到服务器,服务器使用该信息来识别用户。
本节将指导你如何设置或重置 cookie,如何访问 cookie 以及如何删除 cookie。
Cookie 的解剖
Cookies通常设置在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可以访问Cookie,通过请求方法request.getCookies()返回一个Cookie对象数组。
Servlet Cookie方法
以下是有用的方法列表,您可以在Servlet中使用它来操作Cookie。
编号 | 方法和描述 |
---|---|
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。 |
使用Servlet设置Cookie
使用Servlet设置Cookie涉及三个步骤−
(1) 创建一个Cookie对象 − 您对Cookie构造函数调用一个Cookie名称和一个Cookie值,它们都是字符串。
Cookie cookie = new Cookie("key","value");
请记住,名称和值均不应包含空格或以下任何字符−
[ ] ( ) = , " / ? @ : ;
(2) 设置最大生存时间 − 您使用setMaxAge来指定Cookie应有效的时长(以秒为单位)。以下将设置一个24小时的Cookie。
cookie.setMaxAge(60 * 60 * 24);
(3) 将Cookie发送到HTTP响应头中 − 您使用response.addCookie将Cookie添加到HTTP响应头中,如下所示−
response.addCookie(cookie);
示例
让我们修改我们的表单示例,以设置名字和姓氏的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 目录下。当你访问 https://127.0.0.1:8080/Hello.htm 时,这里有以上表单的实际输出。
尝试输入名字和姓氏,然后单击提交按钮。这将在你的屏幕上显示名字和姓氏,同时还会设置两个 Cookie firstName 和 lastName,下次单击提交按钮时它们将被传回到服务器。
下一部分将向你解释如何在你的 Web 应用程序中访问这些 Cookie。
使用 Servlet 读取 Cookie
若要读取 Cookie,你需要通过调用 HttpServletRequest 的 getCookies() 方法来创建 javax.servlet.http.Cookie 对象的数组。然后循环遍历该数组,并使用 getName() 和 getValue() 方法来访问每个 Cookie 和相关值。
示例
让我们读取在前面的示例中设置的 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”,那么运行 https://127.0.0.1:8080/ReadCookies 将显示以下结果 −
Found Cookies Name and Value
Name : first_name, Value: John
Name : last_name, Value: Player
使用 Servlet 删除 Cookie
删除 Cookie 非常简单。如果你想删除一个 Cookie,那么你只需要遵循以下三个步骤 −
读取一个已经存在的 Cookie 并将其存储在 Cookie 对象中。
使用 setMaxAge() 方法将 Cookie 年龄设置为零,以删除现有 Cookie
将此 Cookie 添加回响应标头。
示例
以下示例将删除一个名为“first_name”的现有 Cookie,当你下次运行 ReadCookies Servlet 时,它将为 first_name 返回 null 值。
// 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 文件中的适当条目。现在运行 https://127.0.0.1:8080/DeleteCookies 将显示以下结果 −
Cookies Name and Value
Deleted cookie : first_name
Name : first_name, Value: John
Name : last_name, Value: Player
现在尝试运行 https://127.0.0.1:8080/ReadCookies,它将只显示一个 Cookie,如下所示 −
Found Cookies Name and Value
Name : last_name, Value: Player
你可以在 Internet Explorer 中手动删除你的 Cookie。从工具菜单开始,然后选择 Internet 选项。若要删除所有 Cookie,请按删除 Cookie。
Servlet - 会话跟踪
HTTP 是一种“无状态”协议,这意味着每次客户端检索一个网页时,客户端都会向该 Web 服务器打开一个单独的连接,并且该服务器自动不会保留以前客户端请求的任何记录。
不过,以下三种方法仍可维护 Web 客户端和 Web 服务器之间的会话 −
Cookie
Web 服务器可以为每个 Web 客户端分配一个唯一的会话 ID 作为 Cookie,并且在来自该客户端的后续请求中,可以使用收到的 Cookie 来识别它们。
这可能不是一种有效的方法,因为很多时候浏览器不支持 Cookie,所以我不建议使用此过程来维护会话。
隐藏的表单字段
Web 服务器可以发送一个隐藏的 HTML 表单字段,其中包含一个唯一的会话 ID,如下所示 −
<input type = "hidden" name = "sessionid" value = "12345">
此条目表示,当表单提交时,指定的名称和值会自动包含在 GET 或 POST 数据中。每次 Web 浏览器发送回请求时,session_id 值都可用于跟踪不同的 Web 浏览器。
这可能是一种跟踪会话的有效方法,但单击常规 (<A HREF...>) 超文本链接不会导致表单提交,所以隐藏表单字段也不能支持常规会话跟踪。
URL 重写
你可以在每个 URL 的末尾附加一些标识会话的额外数据,并且服务器可以将该会话标识符与它已存储的对该会话的数据关联起来。
例如,对于 https://tutorialspoint.com/file.htm;sessionid = 12345,会话标识符附加为 sessionid = 12345,可以在 Web 服务器上访问它以识别客户端。
URL 重写是维持会话的一种更好的方式,它甚至可以在浏览器不支持 Cookie 时生效。URL 重写的缺点是,您必须动态生成每个 URL 才能分配会话 ID,即使是一个简单的静态 HTML 页面也一样。
HttpSession 对象
除了上述三种方式之外,Servlet 还提供 HttpSession 接口,它提供了一种跨越某个 Web 站点的多于一个页面请求或访问来识别用户,并存储该用户相关信息的方法。
Servlet 容器使用此接口在 HTTP 客户端和 HTTP 服务器之间创建一个会话。会话会持续一定时间,跨越用户的多个连接或页面请求。
您可以通过调用 HttpServletRequest 的公有方法 getSession() 获取 HttpSession 对象,如下所示 −
HttpSession session = request.getSession();
在向客户端发送任何文档内容之前,您需要调用 request.getSession()。以下是通过 HttpSession 对象可用的重要方法摘要 −
编号 | 方法和描述 |
---|---|
1 | public Object getAttribute(String name) 此方法返回在此会话中与指定名称绑定的对象,或者如果未绑定任何对象,则返回 null。 |
2 | public Enumeration getAttributeNames() 此方法返回包含绑定到此会话的所有对象的名称的字符串对象 Enumeration。 |
3 | public long getCreationTime() 此方法返回此会话的创建时间,以自 1970 年 1 月 1 日格林威治时间午夜以来经过的毫秒为单位。 |
4 | public String getId() 此方法返回包含分配给此会话的唯一标识符的字符串。 |
5 | public long getLastAccessedTime() 此方法返回会话的最后访问时间,格式为自 1970 年 1 月 1 日格林威治时间午夜以来经过的毫秒。 |
6 | public int getMaxInactiveInterval() 此方法返回会话容器在客户端访问之间保持会话打开的最大时间间隔(秒)。 |
7 | public void invalidate() 此方法使此会话无效,并解绑绑定到它的任何对象。 |
8 | public boolean isNew( 如果客户端尚未了解此会话,或如果客户端选择不加入此会话,此方法将返回 true。 |
9 | public void removeAttribute(String name) 此方法从此会话中移除与指定名称绑定的对象。 |
10 | public void setAttribute(String name, Object value) 此方法使用指定名称将对象绑定到此会话。 |
11 | public void setMaxInactiveInterval(int interval) 此方法指定客户端请求之间的时间(秒),在此时间之后,Servlet 容器将使此会话失效。 |
会话跟踪示例
此示例介绍如何使用 HttpSession 对象查找会话的创建时间和上次访问时间。如果尚未存在,我们将与该请求关联一个新的会话。
// Import required java libraries import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.util.*; // Extend HttpServlet class public class SessionTrack extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 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 文件中创建相应的条目。现在,在第一次运行时,运行 https://127.0.0.1:8080/SessionTrack 将显示以下结果 −
Welcome to my website
Session Infomation
Session info value id 0AE3EC93FF44E3C525B4351B77ABB2D5 Creation Time Tue Jun 08 17:26:40 GMT+04:00 2010 Time of Last Access Tue Jun 08 17:26:40 GMT+04:00 2010 User ID ABCD Number of visits 0
现在尝试第二次运行同一 servlet,它将显示以下结果。
Welcome Back to my website
Session Infomation
info type value id 0AE3EC93FF44E3C525B4351B77ABB2D5 Creation Time Tue Jun 08 17:26:40 GMT+04:00 2010 Time of Last Access Tue Jun 08 17:26:40 GMT+04:00 2010 User ID ABCD Number of visits 1
删除会话数据
处理完用户的会话数据后,您有几个选择 −
删除特定属性 − 您可以调用 public void removeAttribute(String name) 方法来删除与特定键关联的值。
删除整个会话 − 您可以调用 public void invalidate() 方法来丢弃整个会话。
设置会话超时 − 您可以调用public void setMaxInactiveInterval(int interval) 方法来分别设置会话的超时时间。
让用户退出 − 支持 servlets 2.4 的服务器,您可以调用 logout 让客户端退出 Web 服务器,并使所有用户的所有会话失效。
web.xml 配置 − 如果您正在使用 Tomcat,除了上面提到的方法之外,您还可以按照如下方式在 web.xml 文件中配置会话超时。
<session-config> <session-timeout>15</session-timeout> </session-config>
超时以分钟表示,并覆盖 Tomcat 中的默认超时 30 分钟。
Servlet 中的 getMaxInactiveInterval( ) 方法以秒为单位返回该会话的超时期限。因此,如果您的会话在 web.xml 中配置为 15 分钟,getMaxInactiveInterval( ) 会返回 900。
Servlet - 数据库访问
本教程假定您了解 JDBC 应用程序的工作方式。在通过 servlet 访问数据库之前,请确保您设置了适当的 JDBC 环境以及数据库。
有关如何使用 JDBC 访问数据库及其环境设置的更多详细信息,您可以通过我们的JDBC 教程了解更多信息。
首先了解基本概念,让我们创建一个简单表格,并在该表格中创建几条记录,如下所示 −
创建表格
要创建 TEST 数据库中的Employees 表格,请使用以下步骤 −
步骤 1
打开一个命令提示符 并更改为安装目录,如下所示 −
C:\> C:\>cd Program Files\MySQL\bin C:\Program Files\MySQL\bin>
步骤 2
登录到数据库,如下所示
C:\Program Files\MySQL\bin>mysql -u root -p Enter password: ******** mysql>
步骤 3
在TEST 数据库中创建表格Employee,如下所示 −
mysql> use TEST; mysql> create table Employees ( id int not null, age int not null, first varchar (255), last varchar (255) ); Query OK, 0 rows affected (0.08 sec) mysql>
创建数据记录
最后,您在 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>
访问数据库
这里有一个示例,展示了如何使用 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://127.0.0.1/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 https://127.0.0.1:8080/DatabaseAccess 调用此 servlet,该 URL 将显示以下响应 −
Database Result
ID: 100, Age: 18, First: Zara, Last: Ali ID: 101, Age: 25, First: Mahnaz, Last: Fatma ID: 102, Age: 30, First: Zaid, Last: Khan ID: 103, Age: 28, First: Sumit, Last: Mittal
Servlet - 文件上载
Servlet 可以与 HTML 表单标记一起使用,以允许用户将文件上传到服务器。上传的文件可能是文本文件、图像文件或任何文档。
创建文件上传表单
以下 HTM 代码创建了一个上传程序表单。以下是需要注意的重要事项 −
表单method 属性应设置为POST 方法,不能使用 GET 方法
表单enctype 属性应设置为multipart/form-data。
表单action 属性应设置为一个 servlet 文件,该文件将在后端服务器处理文件上传。以下示例正在使用 UploadServlet servlet 上传文件。
要上传单个文件,您应该使用单个 <input .../> 标记,其属性 type="file"。要允许上传多个文件,请包含具有 name 属性不同值的多于一个的 input 标记。浏览器将会与每个标记关联一个浏览按钮。
<html> <head> <title>File Uploading Form</title> </head> <body> <h3>File Upload:</h3> Select a file to upload: <br /> <form action = "UploadServlet" method = "post" enctype = "multipart/form-data"> <input type = "file" name = "file" size = "50" /> <br /> <input type = "submit" value = "Upload File" /> </form> </body> </html>
这将显示以下结果,该结果允许从本地 PC 中选择一个文件,当用户点击“上传文件”时,将提交表单以及选定的文件 −
File Upload: Select a file to upload:
NOTE: This is just dummy form and would not work.
编写后端 servlet
以下 servlet 为 UploadServlet,它将负责接受上传的文件并将其存储在 <Tomcat-installation-directory>/webapps/data。该目录名称也可以使用外部配置添加,例如 web.xml 中的 context-param 元素,如下所示 −
<web-app> .... <context-param> <description>Location to store uploaded file</description> <param-name>file-upload</param-name> <param-value> c:\apache-tomcat-5.5.29\webapps\data\ </param-value> </context-param> .... </web-app>
以下是 UploadServlet 的源代码,它一次可以处理多个文件上传。在继续之前,您需要确保以下事项 −
以下示例取决于 FileUpload,因此请确保您的类路径中具有 commons-fileupload.x.x.jar 文件的最新版本。您可以从 https://commons.apache.org/fileupload/ 下载它。
FileUpload 依赖 Commons IO,因此请确保类路径中包含最新版本的 commons-io-x.x.jar 文件。可以从 https://commons.apache.org/io/ 下载该文件。
在测试以下示例时,应当上传一个大小小于 maxFileSize 的文件,否则文件将无法上传。
请确保提前创建目录 c:\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."); } } }
编译和运行 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 表单上传文件。当你尝试访问 https://127.0.0.1:8080/UploadFile.htm 时,将显示以下结果,它将帮助你从本地计算机上传任何文件。
File Upload:Select a file to upload:
如果 servlet 脚本正常运行,则文件应上传到 c:\apache-tomcat8.0.28\webapps\data\ 目录中。
Servlet - 处理日期
使用 Servlet 最重要的优势之一在于,你可以使用核心 Java 中提供的大多数方法。本教程将带你了解 Java 中提供的 Date 类,该类位于 java.util 包中,此类封装了当前日期和时间。
Date 类支持两个构造函数。第一个构造函数用当前日期和时间初始化该对象。
Date( )
以下构造器接受一个自 1970 年 1 月 1 日午夜以来经过的毫秒数作为参数
Date(long millisec)
获得 Date 对象后,你可以调用以下任何支持方法来处理日期 -
编号 | 方法及说明 |
---|---|
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) 将调用对象的日期值与 date 的值进行比较。如果值相等,则返回 0。如果调用对象早于 date,则返回一个负值。如果调用对象晚于 date,则返回一个正值。 |
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 对象转换为字符串并返回结果。 |
获取当前日期和时间
在 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 https://127.0.0.1:8080/CurrentDate 来调用该 Servlet。这将产生以下结果:
Display Current Date & Time
Mon Jun 21 21:46:49 GMT+04:00 2010
尝试刷新 URL https://127.0.0.1:8080/CurrentDate,每次刷新时你都会发现秒数有所不同。
日期比较
正如我上面提到的,你可以在 Servlet 中使用所有可用的 Java 方法。如果你需要比较两个日期,以下是这些方法:
你可以使用 getTime( ) 来获取自 1970 年 1 月 1 日午夜以来所经过的毫秒数,并为这两个对象获取这些值,然后比较这两个值。
你可以使用 before( )、after( ) 和 equals( ) 方法。因为该月的第 12 天在第 18 天之前,例如,new Date(99, 2, 12).before(new Date (99, 2, 18)) 返回 true。
你可以使用 compareTo( ) 方法,它由 Comparable 接口定义,Date 实现了该接口。
使用 SimpleDateFormat 设置日期格式
SimpleDateFormat 是一个具体的类,用于以区域敏感的方式设置和解析日期。SimpleDateFormat 允许你选择任何用户自定义的日期时间格式模式作为起始模式。
让我们修改上面的示例,如下所示:
// 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 https://127.0.0.1:8080/CurrentDate 来调用该 Servlet。这将产生以下结果:
Display Current Date & Time
Mon 2010.06.21 at 10:06:44 PM GMT+04:00
简单日期格式化代码
要指定时间格式,请使用时间模式字符串。在此模式中,所有 ASCII 字母均保留为模式字母,定义如下:
字符 | 描述 | 示例 |
---|---|---|
G | 时代标志 | AD |
y | 四位数年份 | 2001 |
M | 年份中的月份 | 七月或 07 |
d | 月份中的日期 | 10 |
h | 上午/下午时刻 (1~12) | 12 |
H | 一整天中的时刻 (0~23) | 22 |
m | 小时中的分钟 | 30 |
s | 分钟中的秒 | 55 |
S | 毫秒 | 234 |
E | 周中的日期 | 星期二 |
D | 年份中的日期 | 360 |
F | 月份中的一周中的日期 | 2(7 月中的第二个星期三) |
w | 年份中的星期 | 40 |
W | 月份中的星期 | 1 |
a | 上午/下午标记 | 下午 |
k | 一整天中的时刻 (1~24) | 24 |
K | 上午/下午时刻 (0~11) | 10 |
z | 时区 | 美国东部标准时间 |
' | 换行符文本 | 分隔符 |
" | 单引号 | ` |
有关用于操作日期的常量可用方法的完整列表,你可以参考标准 Java 文档。
Servlet - 页面重定向
页面重定向是一种将客户端发送到实际请求以外的新位置的技术。文档移至新位置时,或可能是由于负载平衡而导致页面重定向通常被使用。
将请求重定向到另一页的最简单的方法是使用响应对象的 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); ....
示例
此示例展示了 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 https://127.0.0.1:8080/PageRedirect 调用此 Servlet。这会将你重定向到 URL http://www.photofuntoos.com。
Servlet - 访问计数器
网页的点击计数器
很多时候,你可能希望知道网站上某个特定页面上的总点击次数。使用 Servlet 来计算这些点击次数非常简单,因为 Servlet 的生命周期由其运行其中的容器控制。
以下是实现基于 Servlet 生命周期的一个简单页面点击计数器的步骤:
在 init() 方法中初始化一个全局变量。
每次调用 doGet() 或 doPost() 方法时,增加全局变量。
需要时,你可在 destroy() 方法中使用数据库表储存全局变量的值。此值可在 servlet 下次初始化时在 init() 方法中读取。此步骤是可选的。
如果你只想统计会话中唯一的页面点击量,则可以使用 isNew() 方法检查同一页面是否已在此会话中点击过。此步骤是可选的。
你可以显示全局计数器的值以显示网站上的总点击量。此步骤也是可选的。
此处我假设 Web 容器不会重新启动。如果重新启动或 servlet 已销毁,点击计数器将重置。
示例
此示例展示如何实现一个简单的页面点击计数器 −
import java.io.*; import java.sql.Date; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class PageHitCounter extends HttpServlet { private int hitCount; public void init() { // 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 调用此 servlet https://127.0.0.1:8080/PageHitCounter。每次刷新此页面,计数器都会加 1,并将显示以下结果 −
Total Number of Hits
6
Hit Counter for a Website:
很多时候,你可能想知道整个网站的总点击量。这在 Servlet 中也是非常简单的,我们可以使用过滤器来实现。
以下是基于过滤器生命周期实现一个简单的网站点击计数器的步骤 −
在过滤器的 init() 方法中初始化一个全局变量。
每次调用 doFilter 方法时将全局变量加 1。
需要时,你可在过滤器的 destroy() 方法中使用数据库表储存全局变量的值。此值可在过滤器下次初始化时在 init() 方法中读取。此步骤是可选的。
此处我假设 Web 容器不会重新启动。如果重新启动或 servlet 已销毁,点击计数器将重置。
示例
此示例展示如何实现一个简单的网站点击计数器 −
// 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 https://127.0.0.1:8080/。每次任何页面获得点击,计数器都会加 1,并且会在日志中显示以下消息 −
Site visits count : 1 Site visits count : 2 Site visits count : 3 Site visits count : 4 Site visits count : 5 ..................
Servlets - 自动页面刷新
考虑一个显示实时游戏比分、股票市场状态或货币兑换比率的网页。对于所有此类类型的页面,你需要使用浏览器的刷新或重新加载按钮定期刷新网页。
Java Servlet 通过提供一种机制让工作变得容易,你可以通过这种机制以这样的方式制作网页,即它可在给定的时间间隔后自动刷新。
刷新网页最简单的方法是使用响应对象的 setIntHeader() 方法。以下是此方法的签名 −
public void setIntHeader(String header, int headerValue)
此方法向浏览器发送标头“刷新”以及一个整数,该整数表示以秒为单位的时间间隔。
自动页面刷新示例
此示例展示了 servlet 如何使用 setIntHeader() 方法设置 刷新 标头执行自动页面刷新。
// 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 https://127.0.0.1:8080/Refresh 调用此 servlet,它会每 5 秒显示一次当前系统时间,如下所示。只需运行 servlet 并等待查看结果 −
Auto Page Refresh using Servlet
Current Time is: 9:44:50 PM
Servlet - 发送电子邮件
使用 Servlet 发送电子邮件很容易,但首先你应在你的机器上安装 JavaMail API和 Java Activation Framework (JAF)。
你可以从 Java 的标准网站下载最新版本的JavaMail (版本 1.2)。
您可以从 Java 的标准网站下载 JAF(1.1.1 版)的最新版本(版本 1.1.1)。
下载并解压这些文件,在新建的顶级目录中,您会找到适用于这两个应用程序的 jar 文件。您需要在 CLASSPATH 中添加 mail.jar 和 activation.jar 文件。
发送一个简单的电子邮件
以下是从您的机器发送简单电子邮件的示例。这里假定您的 本地主机已连接到互联网,有足够的能力发送电子邮件。同时确保 Java 电子邮件 API 包和 JAF 包的所有 jar 文件都可用于 CLASSPATH。
// File Name SendEmail.java import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; public class SendEmail extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Recipient's email ID needs to be mentioned. String to = "[email protected]"; // Sender's email ID needs to be mentioned String from = "[email protected]"; // 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 https://127.0.0.1:8080/SendEmail 调用此 Servlet,它会向给定的电子邮件 ID [email protected] 发送电子邮件并显示以下响应−
Send Email
Sent message successfully....
如果您想向多个收件人发送电子邮件,那么将使用以下方法来指定多个电子邮件 ID −
void addRecipients(Message.RecipientType type, Address[] addresses) throws MessagingException
以下是对参数的描述 −
类型 − 这将设置为 TO、CC 或 BCC。这里的 CC 表示抄送,BCC 表示密送。示例:Message.RecipientType.TO
地址 − 这是电子邮件 ID 数组。您需要在指定电子邮件 ID 时使用 InternetAddress() 方法。
发送一封 HTML 电子邮件
以下是从您的机器发送 HTML 电子邮件的示例。这里假定您的 本地主机已连接到互联网,有足够的能力发送电子邮件。同时确保 Java 电子邮件 API 包和 JAF 包的所有 jar 文件都可用于 CLASSPATH。
除了这里我们使用 setContent() 方法来设置内容(其第二个参数是“text/html”)以指定消息中包含 HTML 内容外,此示例与上一个示例非常相似。
您可以使用此示例发送任意大的 HTML 内容。
// File Name SendEmail.java import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; public class SendEmail extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Recipient's email ID needs to be mentioned. String to = "[email protected]"; // Sender's email ID needs to be mentioned String from = "[email protected]"; // 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 消息。
在电子邮件中发送附件
以下是使用您的机器发送带附件的电子邮件的示例。这里假定您的 本地主机已连接到互联网,有足够的能力发送电子邮件。
// 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 = "[email protected]"; // Sender's email ID needs to be mentioned String from = "[email protected]"; // 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 上发送的消息。
用户身份验证部分
如果需要向电子邮件服务器提供用户 ID 和密码进行身份验证,则可以按如下方式设置这些属性 −
props.setProperty("mail.user", "myuser"); props.setProperty("mail.password", "mypwd");
电子邮件发送机制的其余部分将保持如上所述内容。
Servlet - 打包
包含 WEB-INF 子目录的 Web 应用程序结构对所有 Java Web 应用程序来说都是标准的,并由 servlet API 规范指定。假设顶级目录名为 myapp。此目录结构如下所示 −
/myapp /images /WEB-INF /classes /lib
WEB-INF 子目录包含应用程序的部署描述符,名为 web.xml。所有 HTML 文件都应保存在顶级目录中,即 myapp。对于管理员用户,您会发现 ROOT 目录作为父目录。
用包创建 Servlet
WEB-INF/classes 目录包含所有 servlet 类和其他类文件,其结构与包名称相匹配。例如,如果您具有com.myorg.MyServlet 的完全限定类名,则此 servlet 类必须位于如下目录中 −
/myapp/WEB-INF/classes/com/myorg/MyServlet.class
以下是使用包名com.myorg 创建 MyServlet 类的方法
// 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. } }
编译有包的 Servlet
用包中的类编译不会有什么不同。最简单的方法是将 Java 文件保存在完全限定路径中,如上面提到的类将保存在 com.myorg 中。您还需要在 CLASSPATH 中添加此目录。
假定您的环境已正确设置,进入<Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 目录并如下编译 MyServlet.java
$ javac MyServlet.java
如果 Servlet 依赖于任何其他库,您还必须将这些 JAR 文件包含在您的 CLASSPATH 中。我仅包含了 servlet-api.jar JAR 文件,因为我在 Hello World 程序中不使用任何其他库。
此命令行使用 Sun Microsystems Java 软件开发包 (JDK) 附带的内置 javac 编译器。为了使此命令正常工作,您必须在 PATH 环境变量中包含您正在使用的 Java SDK 的位置。
如果一切顺利,上面的编译会在同一目录中生成MyServlet.class文件。下一部分将说明如何将编译后的servlet部署到生产环境中。
已打包的Servlet部署
默认情况下,Servlet 应用程序位于路径 <Tomcat-installationdirectory>/webapps/ROOT,类文件将驻留在 <Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/classes 中。
如果你有一个com.myorg.MyServlet的全限定类名,那么这个Servlet类必须位于WEB-INF/classes/com/myorg/MyServlet.class中,并且你需要在位于<Tomcat-installationdirectory>/webapps/ROOT/WEB-INF/中的web.xml文件添加以下条目:
<servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.myorg.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/MyServlet</url-pattern> </servlet-mapping>
要创建在 web.xml 文件中可用的 <web-app>...</web-app> 标记内部的上述条目。此表中可能已提供各种条目,但不必理会。
你几乎大功告成了,现在让我们使用<Tomcat-installationdirectory>\bin\startup.bat(在Windows上)或<Tomcat-installationdirectory>/bin/startup.sh(在Linux/Solaris等上)启动tomcat服务器,最后在浏览器的地址栏中键入https://127.0.0.1:8080/MyServlet。如果一切顺利,你将得到以下结果−
Hello World
Servlet - 调试
测试/调试Servlet总是很困难。Servlet倾向于涉及大量的客户端/服务器交互,从而使得错误可能会出现,但难以重现。
这里有一些提示和建议,可以在调试中帮助你。
System.out.println()
System.out.println()很容易用作标记,以测试某个代码段是否正在执行。我们也可以打印出变量值。另外−
由于System对象是核心Java对象的一部分,因此可以在任何地方使用它,而无需安装任何额外的类。这包括Servlet、JSP、RMI、EJB、普通Bean和类以及独立应用程序。
在断点处停止技术停止了正常执行,因此需要更多时间。而将内容写入System.out不会过多干扰应用程序的正常执行流,这使得它在时序至关重要的情况下非常有价值。
以下是使用System.out.println()的语法−
System.out.println("Debugging message");
由以上语法生成的所有消息都将记录在Web服务器日志文件中。
消息记录
务必使用适当的记录方法使用标准记录方法记录所有调试、警告和错误消息。我用log4J记录了所有消息。
Servlet API还提供了一种简单的输出信息的方法,使用log()方法如下−
// 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-installation-directory>/logs中。
日志文件确实给出了新出现的bug或问题的频率的指示。因此,最好在异常的catch子句中使用log()函数,这些异常通常不应发生。
使用JDB调试器
你可以用与调试applet或应用程序相同的jdb命令来调试servlet。
要调试servlet,我们调试sun.servlet.http.HttpServer,并在HttpServer响应浏览器发出的HTTP请求时执行servlet时仔细观察。这与调试applet非常相似。不同之处在于,对于applet,实际调试程序是sun.applet.AppletViewer。
大多数调试器会自动知道如何调试applet,从而隐藏该详细信息。在它们对servlet执行相同操作之前,你必须通过执行以下操作来帮助你的调试器−
设置调试器的类路径,以便它可以找到sun.servlet.http.Http-Server和关联类。
设置调试器的类路径,以便它还可以找到你的servlets和支持类,通常是server_root/servlets和server_root/classes。
通常你不希望类路径中包含server_root/servlets,因为它会禁用servlet重新加载。但是,此包含对于调试很有用。它允许你的调试器在HttpServer中的自定义servlet加载器加载servlet之前在servlet中设置断点。
一旦设置好正确的类路径,就开始调试 sun.servlet.http.HttpServer。你可以在你要调试的任何 servlet 中设置断点,然后使用 Web 浏览器向 HttpServer 发出给定 servlet 的请求(https://127.0.0.1:8080/servlet/ServletToDebug)。你应该看到执行在你的断点处停止。
使用注释
代码中的注释可以用多种方式帮助调试过程。注释在调试过程中还有很多其他用法。
Servlet 使用 Java 注释和单行(// ...)和多行(/* ... */)注释可以暂时删除你的 Java 代码的部分。如果漏洞消失了,仔细查看你刚刚注释的代码并找出问题。
客户端和服务器头
有时当一个 servlet 没有按预期工作时,查看原始 HTTP 请求和响应会很有用。如果你熟悉 HTTP 的结构,你可以阅读请求和响应并确切地知道这些标头的情况。
重要的调试技巧
以下是有关 servlet 调试的一些其他调试技巧列表 -
记住,server_root/classes 不会重新加载,而 server_root/servlets 可能重新加载。
要求浏览器显示正在显示的页面的原始内容。这有助于识别格式问题。这通常是“视图”菜单下的一个选项。
通过强制完全重新加载页面,确保浏览器没有缓存前一个请求的输出。在 Netscape Navigator 中,使用 Shift-Reload;在 Internet Explorer 中,使用 Shift-Refresh。
验证 servlet 的 init() 方法是否采用 ServletConfig 参数并立即调用 super.init(config)。
Servlet - 国际化
在我们继续之前,让我解释三个重要术语 -
国际化 (i18n) - 这意味着让网站能够提供翻译成访问者的语言或国籍的不同内容版本
本地化 (l10n) - 这意味着将资源添加到网站以适应特定的地理或文化区域。
区域设置 - 这是一个特定的文化或地理区域。它通常被称为语言符号,后跟国家符号,它们用下划线分隔。例如,“en_US”表示美国英语区域设置。
在搭建全球网站时,需要注意很多事项。本教程不会对此提供完整的详细信息,但它会为你提供一个很好的示例,说明你如何通过区分其位置(即区域设置)向互联网社区以不同语言提供你的网页。
servlet 可以根据请求者的区域设置选择合适的网站版本,并根据当地语言、文化和要求提供合适的网站版本。以下是返回 Locale 对象的请求对象的方法。
java.util.Locale request.getLocale()
检测区域设置
以下是重要的区域设置方法,你可以使用这些方法来检测请求者的位置、语言和区域设置。下面所有方法都显示在请求者的浏览器中设置的国家名称和语言名称。
编号 | 方法和描述 |
---|---|
1 | String getCountry() 此方法以 ISO 3166 2 字母格式返回此区域设置的国家/地区代码(大写)。 |
2 | String getDisplayCountry() 此方法返回适合向用户显示的区域设置国家/地区名称。 |
3 | String getLanguage() 此方法以 ISO 639 格式返回此语言环境的语言代码(小写)。 |
4 | String getDisplayLanguage() 此方法返回适合用户显示的语言环境语言名称。 |
5 | String getISO3Country() 此方法返回此语言环境的国家/地区的三个字母缩写。 |
6 | String getISO3Language() 此方法返回此语言环境的语言的三个字母缩写。 |
示例
此示例展示如何为某个请求显示语言和关联的国家/地区 −
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.util.Locale; public class GetLocale extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //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>" ); } }
语言设置
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ñ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ñol:" + "</h1>\n" + "<h1>" + "¡Hola Mundo!" + "</h1>\n" + "</body> </html>" ); } }
特定语言环境的日期
可以使用 java.text.DateFormat 类及其静态方法 getDateTimeInstance() 格式化特定于语言环境的日期和时间。以下是展示如何特定于给定的语言环境格式化日期的示例 −
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.util.Locale; import java.text.DateFormat; import java.util.Date; public class DateLocale extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 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>" ); } }
特定语言环境的货币
可以使用 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>" ); } }
特定语言环境的百分比
可以使用 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>" ); } }
Servlet - 标注
到目前为止,你已经了解了 Servlet 如何使用部署描述符 (web.xml 文件) 将你的应用程序部署到 Web 服务器中。Servlet API 3.0 引入了名为 javax.servlet.annotation 的新软件包。它提供了一些注释类型,可用于注释 servlet 类。如果使用注释,则不需要部署描述符 (web.xml)。但是你应该使用 tomcat7 或任何更高版本的 tomcat。
注释可以替代 Web 部署描述符文件 (web.xml) 中等效的 XML 配置,如 servlet 声明和 servlet 映射。Servlet 容器将在部署时处理带注释的类。
Servlet 3.0 中引入的注释类型有 −
编号 | 注释和描述 |
---|---|
1 | @WebServlet 声明 servlet。 |
2 | @WebInitParam 指定初始化参数。 |
3 | @WebFilter 声明 servlet 过滤器。 |
4 | @WebListener 声明 WebListener |
5 | @HandlesTypes 声明 ServletContainerInitializer 可以处理的类类型。 |
6 | @HttpConstraint 此注释在 ServletSecurity 注释内使用,以表示要应用到所有 HTTP 协议方法的安全约束,ServletSecurity 注释内没有相应的 HttpMethodConstraint 元素。 |
7 | @HttpMethodConstraint 此注释在 ServletSecurity 注释内使用,以表示对特定 HTTP 协议消息的安全约束。 |
8 | @MultipartConfig 可以在 Servlet 类中指定的注释,表示 Servlet 的实例期望符合 multipart/form-data MIME 类型的请求。 |
9 | @ServletSecurity 在 Servlet 实现类中使用此注释指定 Servlet 容器对 HTTP 协议消息强制执行的安全约束。 |
在此,我们详细讨论了一些注释。
@WebServlet
@WebServlet 用于使用容器声明 Servlet 的配置。下表包含了 WebServlet 注解中使用的属性列表。
编号 | 属性 & 说明 |
---|---|
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 的显示名称,如果存在的话 |
至少一个 URL 模式必须在注解的 value 或 urlPattern 属性中声明,但不能同时存在。
当 URL 模式是唯一要设置的属性时,建议使用 value 属性,否则应使用 urlPattern 属性。
示例
以下示例介绍如何使用 @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 中。
现在尝试通过仅仅运行 https://127.0.0.1:8080/Simple 来调用任何 servlet。你将在网页上看到以下输出。
Hello servlet
@WebInitParam
@WebInitParam 注解用于为 Servlet 或 Filter 指定一个初始化参数。它在 WebFilter 或 WebSevlet 注解中使用。下表包含了 WebInitParam 注解中使用的属性列表。
编号 | 属性 & 说明 |
---|---|
1 | String name 初始化参数的名称 |
2 | String value 初始化参数的值 |
3 | String description 初始化参数的描述 |
示例
以下示例介绍如何将 @WebInitParam 注解与 @WebServlet 注解一起使用。它是一个简单的servlet,显示文本 Hello Servlet 和从 init 参数中获取的字符串值 Hello World!。
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,并将你的类文件放在 <Tomcatinstallationdirectory>;/webapps/ROOT/WEB-INF/classes 中。
现在尝试通过仅仅运行 https://127.0.0.1:8080/Simple 来调用任何 servlet。你将在网页上看到以下输出。
Hello Servlet Hello World!
@Webfilter
这是用于声明 servlet 过滤器的注解。它在部署时由容器处理,并将相应的过滤器应用到指定的 URL 模式、servlet 和调度程序类型中。
@WebFilter 注解在 Web 应用程序中定义过滤器。此注解指定在类中,包含有关正在声明的过滤器的元数据。带注解的过滤器必须指定至少一个 URL 模式。下表列出了 WebFilter 注解中使用的属性。
编号 | 属性 & 说明 |
---|---|
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 此过滤器的图标,如果存在的话 |
示例
以下示例介绍了如何使用 @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 中。
现在尝试通过仅仅运行 https://127.0.0.1:8080/Simple 来调用任何 servlet。你将在网页上看到以下输出。
Hello Servlet Hello World!
现在,打开 servlet 控制台。在那里,您将找到 init 参数 testparam 和 当前时间戳 的值以及 servlet 通知消息。