【Java学习路线之JavaWeb】Servlet教程

建议在看这篇教程之前,可以看一下:使用IDEA创建servlet JavaWeb 应用及使用Tomcat本地部署,这样我们就可以打好了开发环境,方便我们后面的学习。

本教程转载整理于:http://c.biancheng.net/servlet2/

文章目录

  • Servlet教程
    • 为什么要学习Servlet?
    • 读者
    • 阅读条件
  • Servlet到底是什么(非常透彻)
    • Servlet 是一种规范
    • Servlet 接口
    • JSP
    • Servlet 版本
    • Applet
  • Servlet容器(Web容器)是什么
    • Web 服务器
        • 运行环境(运行时)
        • 数据库
        • 总结
    • Web 容器
        • 总结
  • Tomcat下载和安装(图解)
    • Tomcat 下载和安装
        • 安装版 Tomcat
        • 解压版 Tomcat
        • 我的建议
  • Tomcat目录结构
    • 1. bin 目录
    • 2. conf 目录
    • 3. lib 目录
    • 4. logs 目录
    • 5. temp 目录
    • 6. webapps 目录
    • 7. work 目录
  • Servlet三种创建方式
    • Servlet、GenericServlet 、HttpServlet 的关系
    • Servlet 接口
    • GenericServlet 抽象类
    • HttpServlet 抽象类
    • 总结
  • Servlet的部署和访问
      • JavaWeb 应用
    • 部署
        • 1. 进入 Windows DOS 命令行窗口
        • **2. 引入 javax.servlet 包**
        • **3. 编译 Servlet**
        • **4. 创建目录结构**
        • **5. 将 Servlet 移动到 Tomcat 目录中**
        • **6. 配置 web.xml**
    • 访问
  • @WebServlet注解(Servlet注解)
    • @WebServlet 注解的属性
    • @WebServlet 注解的使用
        • 1. 启用注解支持
        • 2. 使用 @WebServlet 注解
    • @WebServlet 注解 和 web.xml 的优缺点
        • @WebServlet 注解配置 Servlet
        • web.xml 配置文件配置 Servlet
  • Servlet生命周期(附带实例)
    • 初始化阶段
        • 1. 加载和实例化 Servlet
        • 2. 调用 init() 方法进行初始化
    • 运行时阶段
    • 销毁阶段
    • Servlet 生命周期执行流程
    • 示例
  • load-on-startup元素:控制Servlet启动优先级
    • 示例
  • Servlet虚拟路径映射
    • Servlet 单一映射
        • 1. web.xml 实现单一映射
        • 2. @WebServlet 实现单一映射
    • Servlet 多重映射
        • 1. 配置多个 元素
        • 2. 配置多个 子元素
        • 3. @WebServlet 实现多重映射
  • Servlet虚拟路径匹配规则
    • 匹配规则
    • 匹配优先级
        • 示例 1
    • Tomcat 中的缺省 Servlet
        • 示例 2
  • ServletConfig接口详解
    • 获得 ServletConfig 对象
        • 1. 直接从带参的 init() 方法中提取
        • 2. 调用 GenericServlet 提供的 getServletConfig() 方法获得
    • ServletConfig 接口
    • 配置 Servlet 初始化参数
        • 1.使用 web.xml 配置初始化参数
        • 2.使用 @WebServlet 配置初始化参数
    • 获取 Servlet 初始化参数
  • ServletContext接口详解
    • 获得 ServletContext 对象
        • 1. 通过 GenericServlet 提供的 getServletContext() 方法
        • 2. 通过 ServletConfig 提供的 getServletContext() 方法
        • 3. 通过 HttpSession 提供的 getServletContext() 方法
        • 4. 通过 HttpServletRequest 提供的 getServletContext() 方法
    • ServletContext 的应用
      • 1. 获取上下文初始化参数
        • 1) 设置上下文初始化参数
        • 2) 调用接口中方法获取初始化参数
        • 例 1
      • 2. 实现数据通讯
        • 数据通讯的相关方法
        • ServletContext 属性与上下文初始化参数对比
        • 例 2
      • 3. 读取 Web 应用下的资源文件
        • 例 3
  • HttpServletRequest接口详解
    • HttpServletRequest 接口
        • 获取请求行信息
        • 示例 1
        • 获取请求头信息
        • 示例 2
        • 获取 form 表单的数据
        • 示例 3
    • 中文乱码问题
        • POST 请求
        • GET 请求
  • Servlet请求转发
    • 请求转发
        • RequestDispatcher 接口
        • 请求转发的工作原理
        • 请求转发的特点
    • request 域对象
        • 1) 生命周期不同
        • 2) 作用域不同
        • 3) Web 应用中数量不同
        • 4) 实现数据共享的方式不同
    • 示例
  • HttpServletResponse接口详解
    • 响应行相关的方法
    • 响应头相关的方法
    • 响应体相关的方法
    • 示例
    • response 中文乱码问题
      • 使用字节流输出中文
        • 乱码原因:
        • 解决方案:
      • 使用字符流输出中文
        • 乱码原因:
        • 解决方案:

Servlet教程

Servlet 是 Server Applet 的简称,译作“服务器端小程序”。它是一种基于 Java 技术的 Web 组件,运行在服务器端,由 Servlet 容器管理,用来生成动态的 Web 内容。

Servlet 程序其实就是一个按照 Servlet 规范编写的 Java 类。它具有平台独立性,可以被编译成字节码,移植到任何支持 Java 技术的服务器中运行。

Servlet 可以使用所有的 Java API。换句话说就是,Java 能做的事情,Servlet 都能做。

为什么要学习Servlet?

使用 Servlet,您可以获取用户通过网页表单提交的数据、访问数据库,还可以创建动态网页。

在 Servlet 出现之前,CGI(通用网关接口)脚本语言是最常用服务器端编程语言。然而 CGI 程序存在一些缺点,例如:编写困难、对用户请求响应时间较长、以进程的方式运行导致性能受限等。

Servlet 是 SUN 公司作为 CGI 技术的替代品而推出的技术,与 CGI 技术相比,Servlet 具有以下优势:

  • 性能更好:Servlet 中,每个请求由一个轻量级的 Java 线程处理,效率更高,性能更好;
  • 可移植性好: 基于Java,具有平台独立性,可移植到任何支持 Java 的服务器中运行;
  • 功能更加强大:Servlet 能够直接和 Web 服务器交互,并能够在各个程序之间共享数据,使得数据库连接池之类的功能很容易实现;
  • 编写方便:Servlet 提供了大量的实用工具例程,例如自动地解析和解码 HTML 表单数据、读取和设置 HTTP 头、处理 Cookie、跟踪会话状态等。

Servlet 是 CGI 技术的替代品,直接使用 Servlet 开发依旧十分繁琐,因此 SUN 公司又推出了 JSP 技术。JSP 对 Servlet 再次进行了封装,JSP 经过编译后依然是 Servlet。

Servlet 是 Java 语言中编写 Web 服务器扩展功能的重要技术,同时它也是 JSP 技术的底层运行基础。学习 Java Web 开发,首先就要掌握 Servlet 技术。

读者

本 Servlet 教程适合初学者阅读,可以帮助读者了解 Servlet 的基本功能及使用。完成本教程后,您将具备使用 Servlet 开发 Web 应用程序的能力,并为后续学习 JSP 等技术打下坚实的基础。

阅读条件

阅读本教程,您需要掌握 Java 编程基础,如果您对 Java 不了解,请转到《Java基础教程》。

Servlet到底是什么(非常透彻)

Servlet 是 Server Applet 的缩写,译为“服务器端小程序”,是一种使用 Java 语言来开发动态网站的技术。

Servlet 虽然被称作“小程序”,但是它的功能却异常强大,因为它是基于 Java 的,几乎可以使用所有的 Java API,Java 能做的事情,Servlet 也能做。

Java 是一种功能强大的通用型编程语言,可以处理 HTTP 请求,可以访问数据库,可以生成 HTML 代码,您完全可以使用原生 Java 来开发动态网站。但是,使用原生 Java 开发动态网站非常麻烦,需要自己解析 HTTP 请求的报头,需要自己分析用户的请求参数,需要自己加载数据库组件……种种原因导致使用原生 Java 开发动态网站几乎是一件不能被接受的事情。正是基于这种原因,Java 官方后来推出了 Servlet 技术,它对开发动态网站需要使用的原生 Java API 进行了封装,形成了一套新的 API,称为 Servlet API。

使用 Servlet 开发动态网站非常方便,程序员只需要集中精力处理业务逻辑,不需要再为那些基础性的、通用性的功能编写代码,这使得 Servlet 在动态网站开发领域具备了很高的实用性。

Servlet 基于 Java,可以使用几乎全部的 Java API,所以它的功能异常强大,完全可以胜任企业级开发,能够处理那些高并发、大吞吐量、业务逻辑复杂的应用场景。

您可以这样理解,Servlet 是 Sun 公司推出的一种基于 Java 的动态网站开发技术。编写 Servlet 代码需要遵循 Java 语法,一个 Servlet 程序其实就是一个按照 Servlet 规范编写的 Java 类。Servlet 程序需要先编译成字节码文件(.class文件),然后再部署到服务器运行。

Servlet 是一种规范

严格来说,Servlet 只是一套 Java Web 开发的规范,或者说是一套 Java Web 开发的技术标准。只有规范并不能做任何事情,必须要有人去实现它。所谓实现 Servlet 规范,就是真正编写代码去实现 Servlet 规范提到的各种功能,包括类、方法、属性等。

Servlet 规范是开放的,除了 Sun 公司,其它公司也可以实现 Servlet 规范,目前常见的实现了 Servlet 规范的产品包括 Tomcat、Weblogic、Jetty、Jboss、WebSphere 等,它们都被称为“Servlet 容器”。Servlet 容器用来管理程序员编写的 Servlet 类。

Servlet 接口

所有的 Servlet 功能都是通过一个名为Servlet的接口(Interface)向外暴露的,编写 Servlet 代码,可以从实现 Servlet 接口开始,就像下面这样:

public class ServletDemo implements Servlet {
    //TODO:
}

直接实现 Servlet 接口比较麻烦,需要实现很多方法,所以 Servlet 规范又提供了两个抽象类,分别是 GenericServlet 类和 HttpServlet 类,它们都实现了 Servlet 接口的很多常用功能。和 GenericServlet 类相比,HttpServlet 类更加方便,所以实际开发中一般都继承自 HttpServlet 类。

JSP

Servlet 是第一代 Java Web 开发技术,它将 HTML 代码以字符串的形式向外输出,编写 HTML 文档就是在拼接字符串,非常麻烦,所以 Java 官方又推出了第二代 Web 开发技术——JSP。

JSP 才是现代化的 Web 开发技术,它允许 HTML 代码和 JSP 代码分离,让程序员能够在 HTML 文档中直接嵌入 JSP 代码。

现在没有人直接使用 Servlet 开发动态网站,大家都转向了 JSP 阵营。但是 JSP 依赖于 Servlet,用户访问 JSP 页面时,JSP 代码会被翻译成 Servlet 代码,最终,HTML 代码还是以字符串的形式向外输出的。您看,JSP 只是在 Servlet 的基础上做了进一步封装。

JSP 代码可以调用 Servlet 类,程序员可以将部分功能在 Servlet 中实现,然后在 JSP 中调用即可。

总之,Servlet 是 JSP 的基础,Servlet 虽然不直接面向用户,但是它依然是 JSP 的后台支撑,想玩转 JSP,必须先玩转 Servlet。

学习顺序:

  • 学习 Servlet 的正确顺序是:Java --> Servlet。
  • 学习 JSP 的正确顺序是:Java --> Servlet --> JSP。

Servlet 版本

截止到 2020 年 10 月,最新的 Servlet 版本是 4.0。下表列出了各种主要 Servlet 版本的发布日期及其对应的 Java 版本。

Servlet 版本 发布日期 JAVA EE/JDK 版本
Servlet 4.0 2017年10月 Java EE 8
Servlet 3.1 2013年5月 Java EE 7
Servlet 3.0 2009年12月 Java EE 6 / Java SE 6
Servlet 2.5 2005年10月 Java EE 5 / Java SE 5
Servlet 2.4 2003年11月 J2EE 1.4 / J2SE 1.3
Servlet 2.3 2001年8月 J2EE 1.3 / J2SE 1.2
Servlet 2.2 1999年8月 J2EE 1.2 / J2SE 1.2
Servlet 2.1 1998年11月 未指定
Servlet 2.0 JDK 1.1
Servlet 1.0 1997年6月

Applet

您可能还听说过 Applet,它和 Servlet 是相对的:

  • Java Servlet 是“服务器端小程序”,运行在服务器上,用来开发动态网站;
  • Java Applet 是“客户端小程序”,一般被嵌入到 HTML 页面,运行在支持 Java 的浏览器中。

Applet 和 Servlet 都是基于 Java 的一种技术,功能都非常强大,但是 Applet 开发步骤繁杂,而且只能在安装 Java 虚拟机(JVM)的计算机上运行,现在已经被 JavaScript 全面替代,几乎没有人再学习 Applet。

Servlet容器(Web容器)是什么

您可能已经知道,部署动态网站一般需要 Web 服务器的支持,例如:

  • 运行 PHP 网站一般选择 Apache 或者 Nginx;
  • 运行 ASP/ASP.NET 网站一般选择 IIS;
  • 运行 Python 网站一般选择内置的 WSGI 服务器模块——wsgiref。

Web 服务器是一种对外提供 Web 服务的软件,它可以接收浏览器的 HTTP 请求,并将处理结果返回给浏览器。

在部署 Servlet 网站时,同样需要一种类似的软件,例如 Tomcat、Jboss、Jetty、WebLogic 等,但是它们通常被称为“容器”,而不是“服务器”,这究竟是为什么呢?Servlet 容器和传统意义上的服务器有什么不同呢?

本节我们先讲解传统 Web 服务器的架构模式,再讲解 Servlet 容器的架构模式,然后将它们进行对比,加深读者的理解。

Web 服务器

初学者可能认为,只要有 Web 服务器,我们编写的网站代码就可以运行了,就可以访问数据库了,就可以注册登录并发布文章了,这其实是一种误解。

我们通常所说的 Web 服务器,比如 Apache、Nginx、IIS 等,它们的功能往往都比较单一,只能提供 http(s) 服务,让用户访问静态资源(HTML 文档、图片、CSS 文件、JavaScript 文件等),它们不能执行任何编程语言,也不能访问数据库,更不能让用户注册和登录。

也就是说,如果只有 Web 服务器,那您只能部署静态网站,不能部署动态网站。要想部署动态网站,必须要有编程语言运行环境(运行时,Runtime)的和数据库管理系统的支持。

运行环境(运行时)

开发网站使用的编程语言一般都是脚本语言(比如 PHP、ASP、Python),部署网站时都是将源代码直接扔到服务器上,然而源代码自己并不能运行,必须要有解释器的支持;当用户访问动态页面时,解释器负责分析、编译和执行源代码,然后得到处理结果。

解释器是执行脚本语言的核心部件,除此以外还有一些辅助性的部件,例如:

  • 垃圾回收器:负责及时释放不需要的内存,腾出资源供其它页面使用;
  • 标准库:任何编程语言都会附带标准库,它们提供了很多通用性的功能,极大地提高了开发效率,避免重复造轮子。

我们习惯将以上各种支持脚本语言运行的部件统称为运行环境,或者运行时(Runtime)。

数据库

Web 服务器不带数据库,编程语言也不带数据库,数据库是一款独立的软件;要想实现用户注册、发布文章、提交评论等功能,就必须安装一款数据库,比如 MySQL、Oracle、SQL Server 等。

总结

部署动态网站一般至少需要三个组件,分别是 Web 服务器、脚本语言运行时和数据库,例如,部署 PHP 网站一般选择「Apache + PHP 运行时 + MySQL」的组合。

img

Web 容器

我们知道,Servlet 是基于 Java 语言的,运行 Servlet 必然少不了 JRE 的支持,它负责解析和执行字节码文件(.class文件)。然而 JRE 只包含了 Java 虚拟机(JVM)、Java 核心类库和一些辅助性性文件,它并不支持 Servlet 规范。要想运行 Servlet 代码,还需要一种额外的部件,该部件必须支持 Servlet 规范,实现了 Servlet 接口和一些基础类,这种部件就是 Servlet 容器。

Servlet 容器就是 Servlet 代码的运行环境(运行时),它除了实现 Servlet 规范定义的各种接口和类,为 Servlet 的运行提供底层支持,还需要管理由用户编写的 Servlet 类,比如实例化类(创建对象)、调用方法、销毁类等。

Servlet 中的容器和生活中的容器是类似的概念:生活中容器用来装水、装粮食,Servlet 中的容器用来装类,装对象。

读者可能会提出疑问,我们自己编写的 Servlet 类为什么需要 Servlet 容器来管理呢?这是因为我们编写的 Servlet 类没有 main() 函数,不能独立运行,只能作为一个模块被载入到 Servlet 容器,然后由 Servlet 容器来实例化,并调用其中的方法。

一个动态页面对应一个 Servlet 类,开发一个动态页面就是编写一个 Servlet 类,当用户请求到达时,Servlet 容器会根据配置文件(web.xml)来决定调用哪个类。

下图演示了 Servlet 容器在整个 HTTP 请求流程中的位置:

【Java学习路线之JavaWeb】Servlet教程_第1张图片

您看,Web 服务器是整个动态网站的“大门”,用户的 HTTP 请求首先到达 Web 服务器,Web 服务器判断该请求是静态资源还是动态资源:如果是静态资源就直接返回,此时相当于用户下载了一个服务器上的文件;如果是动态资源将无法处理,必须将该请求转发给 Servlet 容器。

Servlet 容器接收到请求以后,会根据配置文件(web.xml)找到对应的 Servlet 类,将它加载并实例化,然后调用其中的方法来处理用户请求;处理结束后,Servlet 容器将处理结果再转交给 Web 服务器,由 Web 服务器将处理结果进行封装,以 HTTP 响应的形式发送给最终的用户。

常用的 Web 容器有 Tomcat、Jboss、Jetty、WebLogic 等,其中 Tomcat 由 Java 官方提供,是初学者最常使用的。

为了简化部署流程,Web 容器往往也会自带 Web 服务器模块,提供基本的 HTTP 服务,所以您可以不用再安装 Apache、IIS、Nginx 等传统意义上的服务器,只需要安装一款 Web 容器,就能部署 Servlet 网站了。正是由于这个原因,有的教材将 Tomcat 称为 Web 容器,有的教材又将 Tomcat 称为 Web 服务器,两者的概念已经非常模糊了。

将 Web 容器当做服务器使用后,上面的流程图就变成了下面的样子:

【Java学习路线之JavaWeb】Servlet教程_第2张图片

注意,Servlet 容器自带的 Web 服务器模块虽然没有传统的 Web 服务器强大,但是也足以应付大部分开发场景,对初学者来说是足够的。当然,您也可以将传统的 Web 服务器和 Servlet 容器组合起来,两者分工协作,各司其职,共同完成 HTTP 请求。

总结

Servlet 容器就是 Servlet 程序的运行环境,它主要包含以下几个功能:

  • 实现 Servlet 规范定义的各种接口和类,为 Servlet 的运行提供底层支持;
  • 管理用户编写的 Servlet 类,以及实例化以后的对象;
  • 提供 HTTP 服务,相当于一个简化的服务器。

Tomcat下载和安装(图解)

Servlet 是基于 Java 的动态网站开发技术,它的所有类和组件都是基于 Java 实现的,要想使用 Servlet,就必须提前配置好 Java 运行环境。相信各位读者都已经掌握了如何搭建 Java 环境,此处就不再赘述了,不了解的读者请转到:

  • Java JDK下载与安装教程
  • Java JDK环境变量配置

搭建好了 Java 环境,再安装一款 Web 容器就可以运行 Servlet 代码了。市场上有很多 Web 容器,著名的有 Tomcat、Jetty、JBoss、WebLogic 等,初学者最常使用的是 Tomcat。Tomcat 是 Java 官方推出一款免费开源的 Web 容器,它占用资源少,扩展性好,运行稳定,支持负载均衡,并且易于学习和使用,所以成为最受欢迎的 Tomcat 容器。

本文将为您讲解 Tomcat 的下载和安装。

Tomcat 下载和安装

进入 Tomcat 官网(http://tomcat.apache.org/),在首页左侧的导航栏中找到“Download”分类,可以看到在它下面有多个版本的 Tomcat,如图1所示。

【Java学习路线之JavaWeb】Servlet教程_第3张图片
图1:选择 Tomcat 版本

不同的 Tomcat 版本支持的 Java 版本也不同,读者可以根据自己的 JDK/JRE 版本来选择对应的 Tomcat 版本,如下图所示:

Tomcat 版本 最新子版本 Servlet 规范 JSP 规范 EL 规范 WebSocket 规范 认证(JASIC) 规范 Java 版本 JDK/JRE 版本
10.0.x(内测) 10.0.0 5.0 3.0 4.0 2.0 2.0 8 以及更高
9.0.x 9.0.36 4.0 2.3 3.0 1.1 1.1 8 以及更高
8.5.x 8.5.56 3.1 2.3 3.0 1.1 1.1 7 以及更高
8.0.x(已被取代) 8.0.53(已被取代) 3.1 2.3 3.0 1.1 N/A 7 以及更高
7.0.x 7.0.104 3.0 2.2 2.2 1.1 N/A 6 以及更高 (对于 WebSocket,支持 7 以及更高)
6.0.x(已废弃) 6.0.53(已废弃) 2.5 2.1 2.1 N/A N/A 5 以及更高
5.5.x(已废弃) 5.5.36(已废弃) 2.4 2.0 N/A N/A N/A 1.4 以及更高
4.1.x(已废弃) 4.1.40(已废弃) 2.3 1.2 N/A N/A N/A 1.3 以及更高
3.3.x(已废弃) 3.3.2(已废弃) 2.2 1.1 N/A N/A N/A 1.1 以及更高

我的本地电脑使用的是 JDK 8,对应的 Tomcat 版本是 10 和 9,但是考虑到 Tomcat 10 还在内测阶段,可能不太稳定,所以我选择了 Tomcat 9。

如图1所示,点击”Tomcat 9”链接即可跳转到 Tomcat 9 下载页面,向下滚动鼠标就能看到具体的下载链接,请读者根据自己的操作系统选择对应的版本,如图2所示。

【Java学习路线之JavaWeb】Servlet教程_第4张图片
图2:Tomcat 9 下载页面

Windows 下的 Tomcat 有安装版和解压版,下面说一下它们的区别。

安装版 Tomcat

安装版 Tomcat 下载后得到一个 exe 安装包,和其它软件的安装流程没有什么区别。

安装版 Tomcat 在安装过程中会自动进行相关配置,比如找到 JDK 的位置、读取环境变量 JAVA_HOME 等。安装版 Tomcat 在启动后以服务的方式运行,并且带有 GUI 管理工具(图形界面化的管理工具),用户可以很方便地通过界面进行管理和操作,非常友好。

解压版 Tomcat

解压版 Tomcat 下载后得到一个 zip 压缩包,是免安装的,使用 WinRAR 等软件解压到任意目录后就可以直接使用。

解压版 Tomcat 还可以到处移动,您可以将它从 A 文件夹移动到 B 文件夹,甚至移动到 U 盘,是真正的绿色版。当您不再使用时,直接删除文件夹即可卸载解压版 Tomcat。

我的建议

我建议读者使用解压版 Tomcat,除了安装和卸载方便,解压版 Tomcat 还没有 GUI 界面,只能通过使用各种配置文件和命令来操作和管理,这利于初学者学习 Tomcat 的启动和运行流程。这也是大部分 Tomcat 教材的建议。

我的电脑是 64 位,下载 64 位 Windows 解压版后得到 apache-tomcat-9.0.39-windows-x64.zip,将其解压缩到计算机某磁盘(最好不要选择 C 盘)即可。

【Java学习路线之JavaWeb】Servlet教程_第5张图片
图3:解压 Tomcat

解压缩完成后,打开 Tomcat 所在目录,可以看到它包含了很多子文件夹,它们的作用如下表所示:

子目录 说明
bin 命令中心(启动命令,关闭命令……)
conf 配置中心(端口号,内存大小……)
lib Tomcat 的库文件。Tomcat 运行时需要的 jar 包所在的目录。
logs 存放日志文件。
temp 存储临时产生的文件,即缓存。
webapps 存放项目的文件,web 应用放置到此目录下浏览器可以直接访问。
work 编译以后的 class 文件。

Tomcat目录结构

Tomcat 解压完成以后会看到如表1所示的很多目录,这些目录都有各自的用途,初学者有必要了解一下。

子目录 说明
bin 命令中心(启动命令,关闭命令……)
conf 配置中心(端口号,内存大小……)
lib Tomcat 的库文件。Tomcat 运行时需要的 jar 包所在的目录。
logs 存放日志文件。
temp 存储临时产生的文件,即缓存。
webapps 存放项目的文件,web 应用放置到此目录下浏览器可以直接访问。
work 编译以后的 class 文件。

1. bin 目录

bin 目录用来存放 Tomcat 命令,主要分为两大类,一类是以.sh结尾的 Linux 命令,另一类是以.bat结尾的 Windows 命令。很多环境变量都在此处设置,例如 JDK 路径、Tomcat 路径等。

【Java学习路线之JavaWeb】Servlet教程_第6张图片
图1:bin 目录包含的内容

下面是几个常用的 Tomcat 命令:

  • startup.sh/startup.bat:用来启动 Tomcat;
  • shutdown.sh/shutdown.bat:用来关闭 Tomcat;
  • catalina.bat/ catalina.bat:用来设置 Tomcat 的内存。

2. conf 目录

conf 目录主要是用来存放 Tomcat 的配置文件,如下图所示:

【Java学习路线之JavaWeb】Servlet教程_第7张图片
图2:conf 目录包含的内容

下面是常用到的几个文件:

  • server.xml 用来设置域名、IP、端口号、默认加载的项目、请求编码等;
  • context.xml 用来配置数据源等;
  • tomcat-users.xml 用来配置和管理 Tomcat 的用户与权限;
  • web.xml 可以设置 Tomcat 支持的文件类型;
  • 在 Catalina 目录下可以设置默认加载的项目。

3. lib 目录

lib 目录主要用来存放 Tomcat 运行需要加载的 jar 包。

【Java学习路线之JavaWeb】Servlet教程_第8张图片
图3:lib 目录包含的内容

4. logs 目录

logs 目录用来存放 Tomcat 在运行过程中产生的日志文件,清空该目录中的文件不会对 Tomcat 的运行带来影响。

在 Windows 系统中,控制台的输出日志在 catalina.xxxx-xx-xx.log 文件中;在 Linux 系统中,控制台的输出日志在 catalina.out 文件中。

5. temp 目录

temp 目录用来存放 Tomcat 在运行过程中产生的临时文件,清空该目录中的文件不会对 Tomcat 的运行带来影响。

temp 目录包含的内容
图4:temp 目录包含的内容

6. webapps 目录

webapps 目录用来存放应用程序(也就是通常所说的网站),当 Tomcat 启动时会去加载 webapps 目录下的应用程序,我们编写的 Servlet 程序就可以放在这里。Tomcat 允许以文件夹、war 包、jar 包的形式发布应用。

【Java学习路线之JavaWeb】Servlet教程_第9张图片
图5:webapps 目录包含的内容

7. work 目录

work 目录用来存放 Tomcat 在运行时的编译文件(也即 class 字节码文件),例如 JSP 编译后的文件。清空 work 目录,然后重启 Tomcat,可以达到清除缓存的作用。

Servlet三种创建方式

在 Servlet 中,一个动态网页对应一个 Servlet 类,我们可以通过 web.xml 配置文件将 URL 路径和 Servlet 类对应起来。访问一个动态网页的过程,实际上是将对应的 Servlet 类加载、实例化并调用相关方法的过程;网页上显示的内容,就是通过 Servlet 类中的某些方法向浏览器输出的 HTML 语句。

所以,使用 Servlet 创建动态网页的第一步,就是创建 Servlet 类。

Servlet 规范的最顶层是一个名为 javax.servlet.Servlet 的接口,所有的 Servlet 类都要直接或者间接地实现该接口。直接实现 Servlet 接口不太方便,所以 Servlet 又内置了两个 Servlet 接口的实现类(抽象类),分别为 GenericServlet 和 HttpServlet,因此,创建 Servlet 类有如下三种方式:

  1. 实现 javax.servlet.Servlet 接口,重写其全部方法。
  2. 继承 javax.servlet.GenericServlet 抽象类,重写 service() 方法。
  3. 继承 javax.servlet.http.HttpServlet 抽象类,重写 doGet() 或 doPost() 方法。

Servlet、GenericServlet 、HttpServlet 的关系

下图展示了 Servlet、GenericServlet 以及 HttpServlet 三者之间的关系,其中 MyServlet 是我们自定义的 Servlet 类。

【Java学习路线之JavaWeb】Servlet教程_第10张图片

由上图可知:

  1. GenericServlet 是实现了 Servlet 接口的抽象类。
  2. HttpServlet 是 GenericServlet 的子类,具有 GenericServlet 的一切特性。
  3. Servlet 程序(MyServlet 类)是一个实现了 Servlet 接口的 Java 类。

Servlet 接口

javax.servlet.Servlet 是 Servlet API 的核心接口,所有的 Servlet 类都直接或间接地实现了这一接口。

Servlet 接口中定义了 5 个方法,下面我们对他们做简单的介绍。

返回值 方法 备注
void init(ServletConfig config) Servlet 实例化之后,由 Servlet 容器调用,用来初始化 Servlet 对象。该方法只能被调用一次。 参数 config 用来向 Servlet 传递配置信息。
void service(ServletRequest req,ServletResponse res) Servlet 容器调用该方法处理客户端请求。
void destroy() 服务器关闭、重启或者 Servlet 对象被移除时,由 Servlet 容器调用,负责释放 Servlet 对象占用的资源。
ServletConfig getServletConfig() 该方法用来获取 ServletConfig 对象,该对象中包含了 Servlet 的初始化参数。
String getServletInfo() 该方法用于获取 Servlet 的信息,例如作者、版本、版权等。

示例 1

通过实现 Servlet 接口创建 Servlet,示例代码如下。

package net.biancheng.www;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServlet implements Servlet {
    //Servlet 实例被创建后,调用 init() 方法进行初始化,该方法只能被调用一次
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
    }
    //返回 ServletConfig 对象,该对象包含了 Servlet 的初始化参数
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
    //每次请求,都会调用一次 service() 方法
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //设置字符集
        servletResponse.setContentType("text/html;charset=UTF-8");
        //使用PrintWriter.write()方法向前台页面输出内容
        PrintWriter writer = servletResponse.getWriter();
        writer.write("编程帮欢迎您的到来,网址: www.biancheng.net");
        writer.close();
    }
    //返回关于 Servlet 的信息,例如作者、版本、版权等
    @Override
    public String getServletInfo() {
        return null;
    }
    //Servelet 被销毁时调用
    @Override
    public void destroy() {
    }
}

GenericServlet 抽象类

javax.servlet.GenericServlet 实现了 Servlet 接口,并提供了除 service() 方法以外的其他四个方法的简单实现。通过继承 GenericServlet 类创建 Servlet ,只需要重写 service() 方法即可,大大减少了创建 Servlet 的工作量。

GenericServlet 类中还提供了以下方法,用来获取 Servlet 的配置信息。

返回值 方法 备注
String getInitParameter(String name) 返回名字为 name 的初始化参数的值,初始化参数在 web.xml 中进行配置。如果参数不存在,则返回 null。
Enumeration getInitParameterNames() 返回 Servlet 所有初始化参数的名字的枚举集合,若 Servlet 没有初始化参数,返回一个空的枚举集合。
ServletContext getServletContext() 返回 Servlet 上下文对象的引用。
String getServletName() 返回此 Servlet 实例的名称。

示例 2

通过继承 GenericServlet 抽象类创建 Servlet,示例代码如下。

package net.biancheng.www;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //设置字符集
        servletResponse.setContentType("text/html;charset=UTF-8");
        //使用PrintWriter.write()方法向前台页面输出内容
        PrintWriter writer = servletResponse.getWriter();
        writer.write("编程帮欢迎您的到来,网址: www.biancheng.net");
        writer.close();
    }
}

HttpServlet 抽象类

javax.servlet.http.HttpServlet 继承了 GenericServlet 抽象类,用于开发基于 HTTP 协议的 Servlet 程序。由于 Servlet 主要用来处理 HTTP 的请求和响应,所以通常情况下,编写的 Servlet 类都继承自 HttpServlet。

在 HTTP/1.1 协议中共定义了 7 种请求方式,即 GET、POST、HEAD、PUT、DELETE、TRACE 和 OPTIONS。

HttpServlet 针对这 7 种请求方式分别定义了 7 种方法,即 doGet()、doPost()、doHead()、doPut()、doDelete()、doTrace() 和 doOptions()。

HttpServlet 重写了 service() 方法,该方法会先获取客户端的请求方式,然后根据请求方式调用对应 doXxx 方法。

示例 3

由于我们使用的请求方式主要是 GET 和 POST,所以通过继承 HttpServlet 类创建 Servlet 时,只需要重写 doGet 或者 doPost 方法,代码如下。

package net.biancheng.www;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //使用PrintWriter.write()方法向前台页面输出内容
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.write("编程帮欢迎您的到来,网址: www.biancheng.net");
        writer.close();
    }
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //使用PrintWriter.write()方法gaifang向前台页面输出内容
        PrintWriter writer = resp.getWriter();
        writer.write("编程帮欢迎您的到来,网址: www.biancheng.net");
        writer.close();
        doGet(req, resp);
    }
}

总结

上面演示了三种创建 Servlet 的方式,那么在实际开发中,我们究竟该选择哪一种呢?下面我们就来分析和对比一下。

  1. Servlet 接口

通过实现 Servlet 接口创建 Servlet 类,需要重写其全部的方法,比较繁琐,所以我们很少使用该方法创建 Servlet。

  1. GenericServlet 类

GenericServlet 抽象类实现了 Servlet 接口,并对 Servlet 接口中除 service() 方法外的其它四个方法进行了简单实现。通过继承 GenericServlet 创建 Servlet,只需要重写 service() 方法即可,大大减少了创建 Servlet 的工作量。

Generic 是“通用”的意思,正如其名,GenericServlet 是一个通用的 Servlet 类,并没有针对某种场景进行特殊处理,尤其是 HTTP 协议,我们必须手动分析和封装 HTPP 协议的请求信息和响应信息。

  1. HttpServlet 类

HttpServlet 是 GenericServlet 的子类,它在 GenericServlet 的基础上专门针对 HTPP 协议进行了处理。HttpServlet 为 HTTP 协议的每种请求方式都提供了对应的方法,名字为 doXxx(),例如:

  • 处理 GET 请求的方法为 doGet();
  • 处理 POST 请求的方法为 doPost()。

正如其名,HttpServlet 就是专为 HTTP 协议而量身打造的 Servlet 类。

在互联网上,人们都是通过 HTTP 协议来访问动态网页的,其中使用最频繁的就是 GET 方式和 POST 方式,因此,我们通常基于 HttpServlet 来创建 Servlet 类,这样就省去了处理 HTTP 请求的过程。

Servlet的部署和访问

Servlet 没有 main() 方法,不能独立运行,但它可以作为 JavaWeb 应用的一个组件被部署到 Servlet 容器中,由容器来实例化和调用 Servlet 的方法,例如:doGet() 、doPost() 等。

那么,JavaWeb 应用是什么呢?Servlet 是如何部署和访问的呢?本节我们将针对这些问题进行讲解。

JavaWeb 应用

JavaWeb 应用由一组 Servlet/JSP、HTML 文件、相关 Java 类、以及其他的资源组成,它可以在由各种供应商提供的 Servlet 容器中运行。由 JavaWeb 应用的定义可知, Servlet 是 JavaWeb 应用的一个组件。

为了让 Servlet 容器顺利地找到 JavaWeb 应用的各个组件,Servlet 规范规定,JavaWeb 应用必须采用固定的目录结构,即每种组件在 JavaWeb 应用中都有固定的存放目录。

以 Tomcat 为例,通常将 JavaWeb 应用存放到 Tomcat 的 webapps 目录下。在 webapps 下,每一个子目录都是一个独立的 Web 应用,子目录的名字就是 Web 应用的名字,也被称为 Web 应用的上下文根。用户可以通过这个上下文根来访问 JavaWeb 应用中的资源。

webapps 的目录结构如下图。

【Java学习路线之JavaWeb】Servlet教程_第11张图片

下表中对 webapps 下各个目录进行了介绍。

目录 描述 是否必需
\servletDemo Web 应用的根目录,属于该 Web 应用的所有资源都存放在这个目录下。
\servletDemo\WEB-INF 存放 web.xml、lib 目录以及 classes 目录等。
\servletDemo\WEB-INF\classes 存放各种 .class 文件或者包含 .class 文件的目录,Servlet 类的 .class 文件也存放在此。
\servletDemo\WEB-INF\lib 存放应用所需的各种 jar 包,例如 JDBC 驱动程序的 jar 包。
\servletDemo\WEB-INF\web.xml web.xml 中包含应用程序的配置和部署信息。

部署

在 Tomcat 中部署 JavaWeb 应用最快捷的方式,就是直接将 JavaWeb 应用的所有文件复制到 Tomcat 的 /webapps 目录下。在默认情况下,Tomcat 会自动加载 webapps 目录下的 JavaWeb 应用,并把它发布到名为 localhost 的虚拟主机中

Tomcat 既可以运行采用开放式目录结构(只编译不打包)的 Web 应用,也可以运行 Web 应用的打包文件(WAR 文件)。在开发阶段,为了方便程序调试,通常采用开放式的目录结构部署 JavaWeb 应用。在开发完成,进入产品发布阶段时,就应该将整个应用打包成 WAR 文件,再进行部署。

即使采用 WAR 文件的形式发布,Tomcat 启动时也会将 WAR 文件自动展开为开放式的目录结构。

在本案例中,我们将采用开放式的目录结构进行部署,具体操作步骤如下。

1. 进入 Windows DOS 命令行窗口

我们知道,编译 Java 源代码一般可以通过两种方式完成:DOS 命令行 和 IDE(集成开发环境)。使用 IDE 可以一键完成编译工作,十分的方便,但 IDE 给我们带来方便的同时,也隐藏了编译过程中的许多细节,我们无法了解其中的原理和机制。

对于初学者来说,为了更加深刻地理解 Servlet 的运行原理和机制,最好的办法就是使用 DOS 命令行进行编译。

使用快捷键 “Windows + R”,打开运行对话框,在对话框输入cmd,点击“确定”,进入 Windows 系统命令行窗口,如图。

【Java学习路线之JavaWeb】Servlet教程_第12张图片

2. 引入 javax.servlet 包

由于 Servlet 是 JavaEE 下的技术标准,不是 JDK 的组成部分,所以在编译 Servlet 前,需要先引入 servlet-api.jar 包(在 Apache Tomcat 安装目录下的 lib 文件夹中提供了此 jar 包)。

使用命令set classpath可以将 servlet-api.jar 引入,该命令的语法如下,其中 path 表示引入 jar 包的路径。

set classpath = path

为了方便操作,可以先在命令行中输入set classpath=,然后将 servlet-api.jar 文件拖进命令行内,回车执行。

【Java学习路线之JavaWeb】Servlet教程_第13张图片

注:set classpath命令用来为当前窗口设置临时环境变量,只在当前窗口下有效。

3. 编译 Servlet

1)在命令提示符中使用 cd命令进入 MyServlet.java 所在的目录。

【Java学习路线之JavaWeb】Servlet教程_第14张图片

2)使用 javac -encoding UTF-8 -d . MyServlet.java命令进行编译,若没有报错,则编译成功。

【Java学习路线之JavaWeb】Servlet教程_第15张图片

其中:

  • javac :编译命令;
  • -encoding UTF-8:用于指定编译源文件时的字符编码,这里指定为 UTF-8;
  • javac -d:带包编译 ;
  • .:表示当前位置。

3)进入 MyServlet.java 文件所在的目录,可发现新增了编译后的目录和 .classes 字节码文件,自此我们就完成了对 Servlet 的编译工作。

【Java学习路线之JavaWeb】Servlet教程_第16张图片

4. 创建目录结构

在完成对 Servlet 的编译后,下一步依照 JavaWeb 应用的固定目录结构,在 Tomcat 中为该 Servlet 创建目录。

  1. 在 Tomcat 的 webapps 目录中创建 servletDemo(web 应用的名字,由自己定义)文件夹;
  2. 在 servletDemo 文件夹内创建 WEB-INF 文件夹;
  3. 在 WEB-INF 内创建 classes 文件夹;
  4. 在 WEB-INF 内创建配置文件 web.xml(可以复制 \webapps\ROOT\WEB-INF 中的 web.xml 使用)。
5. 将 Servlet 移动到 Tomcat 目录中

将编译好的字节码和目录移动到 Tomcat\webapps\servletDemo\WEB-INF\classes 目录下。

【Java学习路线之JavaWeb】Servlet教程_第17张图片

6. 配置 web.xml

对 webapps\servletDemo\WEB-INF 目录的 web.xml 中进行配置,具体配置代码如下。

            MyServlet        net.biancheng.www.MyServlet               MyServlet        /MyServlet    

web.xml 中各元素含义及用法如下:

  • : 根元素。
  • :用于注册 Servlet,即给 Servlet 起一个独一无二的名字。
  • 包含两个主要的子元素 和 ,分别用于指定 Servlet 的名称和 Servlet 的完整限定名(包名+类名)。
  • :用于定义 Servlet 与 URL 之间的映射。
  • 包含两个子元素 和 ,分别用于指定 Servlet 的名称和虚拟路径。

访问

进入 Tomcat\bin 目录下双击 startup.bat,启动 Tomcat。

【Java学习路线之JavaWeb】Servlet教程_第18张图片

Tomcat 启动成功后,在地址栏中输入“http://localhost:8080/servletDemo/MyServlet”,访问 MyServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第19张图片

访问路径 http://localhost:8080/servletDemo/MyServlet 中,各部分含义如下:

  • http:// 表示 HTTP 协议;
  • localhost: 表示服务器 IP;
  • 8080 表示端口号;
  • /servletDemo 表示 Web 应用的上下文根路径;
  • /MyServlet 表示资源路径,即 web.xml 中 元素的取值。

@WebServlet注解(Servlet注解)

在 Servlet 中,web.xml 扮演的角色十分的重要,它可以将所有的 Servlet 的配置集中进行管理,但是若项目中 Servelt 数量较多时,web.xml 的配置会变得十分的冗长。这种情况下,注解(Annotation)就是一种更好的选择。

与 XML 不同,注解不需要依赖于配置文件,它可以直接在类中使用,其配置只对当前类有效,这样就避免了集中管理造成的配置冗长问题。那么 Servelt 支持注解吗?

为了简化 Servlet 的配置,Servlet 3.0 中增加了注解支持,例如:@WebServlet、@WebInitParm 、@WebFilter 和 @WebLitener 等,这使得 web.xml 从 Servlet 3.0 开始不再是必选项了。下面我们对 @WebServlet 进行介绍。

@WebServlet 注解的属性

@WebServlet 用于将一个类声明为 Servlet,该注解会在部署时被容器处理,容器根据其具体的属性配置将相应的类部署为 Servlet。该注解具有下表给出的一些常用属性。

属性名 类型 标签 描述 是否必需
name String 指定 Servlet 的 name 属性。 如果没有显式指定,则取值为该 Servlet 的完全限定名,即包名+类名。
value String[ ] 该属性等价于 urlPatterns 属性,两者不能同时指定。 如果同时指定,通常是忽略 value 的取值。
urlPatterns String[ ] 指定一组 Servlet 的 URL 匹配模式。
loadOnStartup int 指定 Servlet 的加载顺序。
initParams WebInitParam[ ] 指定一组 Servlet 初始化参数。
asyncSupported boolean 声明 Servlet 是否支持异步操作模式。
description String 指定该 Servlet 的描述信息。
displayName String 指定该 Servlet 的显示名。

@WebServlet 注解的使用

1. 启用注解支持

web.xml 的顶层标签 中有一个属性:metadata-complete,该属性用于指定当前 web.xml 是否是完全的。若该属性设置为 true,则容器在部署时将只依赖 web.xml,忽略所有的注解。若不配置该属性,或者将其设置为 false,则表示启用注解支持。


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    id="WebApp_ID" metadata-complete="false" version="4.0">
    
    
web-app>

由于 metadata-complete 属性的默认值是 false,即默认启用 Servlet 注解支持,所以默认情况下,使用该注解时,不必创建 web.xml 文件。

2. 使用 @WebServlet 注解

@WebServlet 属于类级别的注解,标注在继承了 HttpServlet 的类之上。常用的写法是将 Servlet 的相对请求路径(即 value)直接写在注解内,如下所示。

@WebServlet("/MyServlet") 

该写法省略了 urlPatterns 属性名,其完整的写法如下所示。

@WebServlet(urlPatterns = "/MyServlet")。

如果 @WebServlet 中需要设置多个属性,则属性之间必须使用逗号隔开,如下所示。

package net.biancheng.www;
import java.io.IOException;
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(asyncSupported = true, name = "myServlet", description = "name描述", loadOnStartup = 1, urlPatterns = {
        "/MyServlet", "/*" }, initParams = {
                @WebInitParam(name = "编程帮", value = "www.biancheng.net", description = "init参数1"),
                @WebInitParam(name = "京东", value = "www.jd.com", description = "init参数2") })
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    }
}

注意事项:

  • 通过实现 Serlvet 接口或继承 GenericServlet 创建的 Servlet 类无法使用 @WebServlet 注解。
  • 使用 @WebServlet 注解配置的 Servlet 类,不要在 web.xml 文件中再次配置该 Servlet 相关属性。若同时使用 web.xml 与 @WebServlet 配置同一 Servlet 类,则 web.xml 中 的值与注解中 name 取值不能相同,否则容器会忽略注解中的配置。

@WebServlet 注解 和 web.xml 的优缺点

使用 web.xml 或 @WebServlet 注解都可以配置 Servlet, 两者各有优缺点。

@WebServlet 注解配置 Servlet

优点:@WebServlet 直接在 Servlet 类中使用,代码量少,配置简单。每个类只关注自身业务逻辑,与其他 Servlet 类互不干扰,适合多人同时开发。

缺点:Servlet 较多时,每个 Servlet 的配置分布在各自的类中,不便于查找和修改。

web.xml 配置文件配置 Servlet

优点:集中管理 Servlet 的配置,便于查找和修改。

缺点:代码较繁琐,可读性不强,不易于理解。

Servlet生命周期(附带实例)

生命周期是指事物从创建到毁灭的过程。人的生命周期就是从出生到死亡的过程,在这个过程中,必定会有一些与生命周期息息相关的事件,如:出生、吃饭、上学、死亡等。这些事件会在生命周期中的某个特定时刻发生。

Servlet 也有生命周期,Servlet 的生命周期就是 Servlet 从创建到销毁的过程。Servlet 的生命周期由 Servlet 容器管理,主要分为以下 3 个阶段。

  1. 初始化阶段
  2. 运行时阶段
  3. 销毁阶段

在 javax.servlet.Servlet 接口中定义了 3 个方法:init()、service()、destory(),它们分别在 Servlet 生命周期的不同阶段被 Servlet 容器调用。

初始化阶段

Servlet 初始化是其生命周期的第一个阶段,也是其他阶段的基础。只有完成了初始化,Servlet 才能处理来自客户端的请求。

Servlet 初始化阶段分为 2 步:

  1. 加载和实例化 Servlet;
  2. 调用 init() 方法进行初始化。
1. 加载和实例化 Servlet

Servlet 容器负责加载和实例化 Servlet。当容器启动或首次请求某个 Servlet 时,容器会读取 web.xml 或 @WebServlet 中的配置信息,对指定的 Servlet 进行加载。加载成功后,容器会通过反射对 Servlet 进行实例化。

因为 Servlet 容器是通过 Java 的反射 API 来创建 Servlet 实例的,需要调用 Servlet 的默认构造方法(default constructor,即不带参数的构造方法),所以在编写 Servlet 类时,不能只提供一个带参数的构造方法。

2. 调用 init() 方法进行初始化

加载和实例化完成后,Servlet 容器调用 init() 方法初始化 Servlet 实例。

初始化的目的:让 Servlet 实例在处理请求之前完成一些初始化工作,例如建立数据库连接,获取配置信息等。

在 Servlet 的整个生命周期内,init() 方法只能被调用一次。

初始化期间,Servlet 实例可以通过 ServletConfig 对象获取在 web.xml 或者 @WebServlet 中配置的初始化参数。

运行时阶段

运行时阶段是 Servlet 生命周期中最重要的阶段。Servlet 容器接收到来自客户端请求时,容器会针对该请求分别创建一个 ServletRequst 对象和 ServletResponse 对象,将它们以参数的形式传入 service() 方法内,并调用该方法对请求进行处理。

这里需要注意的是,执行 service() 方法前,init() 方法必须已成功执行。

在 service() 方法中,Servlet 通过 ServletRequst 对象获取客户端的相关信息和请求信息。在请求处理完成后,通过 ServletResponse 对象将响应信息进行包装,返回给客户端。当 Servlet 容器将响应信息返回给客户端后,ServletRequst 对象与 ServletResponse 对象就会被销毁。

在 Servlet 的整个生命周期内,对于 Servlet 的每一次请求,Servlet 容器都会调用一次 service() 方法,并创建新的 ServletRequest 和 ServletResponse 对象。即 service() 方法在 Servlet 的整个生命周期中会被调用多次。

销毁阶段

当 Servlet 容器关闭、重启或移除 Servlet 实例时,容器就会调用 destory() 方法,释放该实例使用的资源,例如:关闭数据库连接,关闭文件的输入流和输出流等,随后该实例被 Java 的垃圾收集器所回收。

对于每个 Servlet 实例来说,destory() 方法只能被调用一次。

Servlet 生命周期执行流程

Servlet 生命周期流程如下图所示。

【Java学习路线之JavaWeb】Servlet教程_第20张图片

在 Servlet 的整个生命周期中,创建 Servlet 实例、init() 方法和 destory() 方法都只执行一次。当初始化完成后,Servlet 容器会将该实例保存在内存中,通过调用它的 service() 方法,为接收到的请求服务。

示例

下面通过一个案例加深对 Servlet 生命周期的理解。

\1. 在 servletDemo 项目中,对 MyServlet.java 进行修改,代码如下:

package net.biancheng.www;
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("/MyServlet")
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private int initCount = 0;
    private int httpCount = 0;
    private int destoryCount = 0;
    @Override
    public void destroy() {
        destoryCount++;
        super.destroy();
        // 向控制台输出destory方法被调用次数
        System.out.println(
                "**********************************destroy方法:" + destoryCount + "*******************************");
    }
    @Override
    public void init() throws ServletException {
        initCount++;
        super.init();
        // 向控制台输出init方法被调用次数
        System.out.println("init方法:" + initCount);
    }
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        httpCount++;
        // 控制台输出doGet方法次数
        System.out.println("doGet方法:" + httpCount);
        // 设置返回页面格式与字符集
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        // 向页面输出
        writer.write("初始化次数:" + initCount + "
"
+ "处理请求次数:" + httpCount + "
"
+ "销毁次数:" + destoryCount); writer.close(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }

\2. 启动 Tomcat,在地址栏输入“http://localhost:8080/servletDemo/MyServlet”,多次访问 MyServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第21张图片

\3. 控制台输出,如下图。

【Java学习路线之JavaWeb】Servlet教程_第22张图片

\4. 关闭 Tomcat 服务器,控制台输出如下图。

【Java学习路线之JavaWeb】Servlet教程_第23张图片

load-on-startup元素:控制Servlet启动优先级

load-on-startup 是 web.xml 中的一个节点,是 servlet 元素的子元素,用来标记 Servlet 容器启动时是否初始化当前 Servlet,以及当前 Servlet 的初始化顺序。

load-on-startup 元素取值规则如下:

  1. 它的取值必须是一个整数;
  2. 当值小于 0 或者没有指定时,则表示容器在该 Servlet 被首次请求时才会被加载;
  3. 当值大于 0 或等于 0 时,表示容器在启动时就加载并初始化该 Servlet,取值越小,优先级越高;
  4. 当取值相同时,容器就会自行选择顺序进行加载。

@WebServlet 注解的 loadOnStartup 属性与 web.xml 中的 load-on-startup 元素相对应,取值的规则和含义相同。

示例

下面通过一个案例进行演示。

在 servletDemo 项目的 net.biancheng.www 包中,创建名称为 MyServlet1 的类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
    @Override
    public void destroy() {
        System.out.println(this.getServletName() + ":销毁");
    }
    @Override
    public void init() throws ServletException {
        System.out.println(this.getServletName() + ":初始化完成");
    }
}

web.xml 的配置如下。


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         id="WebApp_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServlet1servlet-name>
        <servlet-class>net.biancheng.www.MyServlet1servlet-class>
        
        <load-on-startup>0load-on-startup>
    servlet>
    <servlet>
        <servlet-name>MyServlet2servlet-name>
        <servlet-class>net.biancheng.www.MyServlet1servlet-class>
        
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet>
        <servlet-name>MyServlet3servlet-name>
        <servlet-class>net.biancheng.www.MyServlet1servlet-class>
        
        <load-on-startup>2load-on-startup>
    servlet>
    <servlet>
        <servlet-name>MyServlet4servlet-name>
        <servlet-class>net.biancheng.www.MyServlet1servlet-class>
        
        <load-on-startup>-1load-on-startup>
    servlet>
    
    <servlet>
        <servlet-name>MyServlet5servlet-name>
        <servlet-class>net.biancheng.www.MyServlet1servlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>MyServlet1servlet-name>
        <url-pattern>/MyServlet1url-pattern>
    servlet-mapping>
    <servlet-mapping>
        <servlet-name>MyServlet2servlet-name>
        <url-pattern>/MyServlet2url-pattern>
    servlet-mapping>
    <servlet-mapping>
        <servlet-name>MyServlet3servlet-name>
        <url-pattern>/MyServlet3url-pattern>
    servlet-mapping>
    <servlet-mapping>
        <servlet-name>MyServlet4servlet-name>
        <url-pattern>/MyServlet4url-pattern>
    servlet-mapping>
    <servlet-mapping>
        <servlet-name>MyServlet5servlet-name>
        <url-pattern>/MyServlet5url-pattern>
    servlet-mapping>
web-app>

启动 Tomcat 服务器,控制台输出如下图所示。

【Java学习路线之JavaWeb】Servlet教程_第24张图片

在地址栏输入“http://localhost:8080/servletDemo/MyServlet1”,访问 MyServlet1,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第25张图片

在地址栏输入“http://localhost:8080/servletDemo/MyServlet2”,访问 MyServlet2,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第26张图片

在地址栏输入“http://localhost:8080/servletDemo/MyServlet3”,访问 MyServlet3,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第27张图片

在地址栏输入“http://localhost:8080/servletDemo/MyServlet4”,访问 MyServlet4,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第28张图片

在地址栏输入“http://localhost:8080/servletDemo/MyServlet5”,访问 MyServlet5,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第29张图片

由示例可知:

  • 由于 MyServlet1、MyServlet2 和 MyServlet3 的 load-on-startup 元素取值都大于等于 0,因此当 Tomcat 启动时,就对它们进行了初始化。
  • 由于在 Servlet 的生命周期内,init() 方法(初始化方法)只能被调用一次,因此当 Tomcat 启动完成后,第一次访问 MyServlet1、MyServlet2 和 MyServlet3 时,它们不会再次被初始化。
  • 由于 MyServlet4 的 load-on-startup 元素取值为负数,因此只有当第一次请求它时,才会进行初始化。
  • 由于 MyServlet5 没有指定 load-on-startup 元素取值,因此只有当第一次请求它时,才会进行初始化。

Servlet虚拟路径映射

客户端通过 URL 地址来访问 Web 服务器中的资源,Servlet 程序若想被外界访问,就必须被映射到一个 URL 地址上。很多时候,该 URL 地址和 Servlet 程序的物理路径(在硬盘上的存储位置)并不一致,因此它被称为虚拟路径。Servlet 与虚拟路径的对应关系就叫做 Servlet 虚拟路径映射。

Servlet 虚拟路径映射可以被分为 2 类:

  1. 单一映射
  2. 多重映射

下面介绍如何实现单一映射和多重映射。

Servlet 单一映射

Servelt 单一映射是指一个 Servlet 只被映射到一个虚拟路径上。

Servlet 单一映射的实现方式有 2 种:

  1. 使用 web.xml 实现单一映射;
  2. 使用 @WebServlet 实现单一映射。
1. web.xml 实现单一映射

在 web.xml 文件中,使用 和 元素实现 Servlet 单一映射,代码如下。


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         id="WebApp_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServletservlet-name>
        <servlet-class>net.biancheng.www.MyServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>MyServletservlet-name>
        <url-pattern>/myServleturl-pattern>
    servlet-mapping>
web-app>

对以上标签说明如下:

  • 元素用于注册 Servlet,即给 Servlet 起一个独一无二的名字。
  • 包含两个主要的子元素 和 ,分别用于指定 Servlet 的名称和 Servlet 的完整限定名(包名+类名)。
  • 元素用于定义 Servlet 与虚拟路径之间的映射。
  • 包含两个子元素 和 ,分别用于指定 Servlet 的名称和虚拟路径。
2. @WebServlet 实现单一映射

在 @WebServlet 注解中,一般使用 value 属性实现 Servlet 单一映射,代码如下。

package net.biancheng.www;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

也可以使用 urlPatterns 属性实现 Servlet 单一映射,代码如下。

package net.biancheng.www;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

Servlet 多重映射

Servlet 的多重映射是指一个 Servlet 可以被映射到多个虚拟路径上。此时,客户端可以通过多个路径实现对同一个 Servlet 的访问。

Servlet 多重映射的实现方式有 3 种:

  1. 配置多个 元素。
  2. 配置多个 子元素。
  3. 在 @WebServlet 的 urlPatterns 属性中使用字符串数组
1. 配置多个 元素

Servlet 2.5 规范之前, 元素只允许包含一个 子元素,若要实现 Servet 的多重映射,只能通过配置多个 元素实现。

以 serveltDemo 为例,在 web.xml 中配置两个 元素,代码如下所示。


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    id="WebApp_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServletservlet-name>
        <servlet-class>net.biancheng.www.MyServletservlet-class>
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>MyServletservlet-name>
        <url-pattern>/myServleturl-pattern>
    servlet-mapping>
    <servlet-mapping>
        <servlet-name>MyServletservlet-name>
        <url-pattern>/myServlet2url-pattern>
    servlet-mapping>
web-app>

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/myServlet”,访问 MyServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第30张图片

在地址栏中输入“http://localhost:8080/servletDemo/myServlet2”,也可以访问 MyServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第31张图片

2. 配置多个 子元素

从 Servlet 2.5 开始, 元素可以包含多个 子元素,每个 代表一个虚拟路径的映射规则。因此,通过在一个 元素中配置多个 子元素,也可以实现 Servlet 的多重映射。

以 servletDemo 为例, 元素下添加两个 子元素,代码如下。


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    id="WebApp_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServletservlet-name>
        <servlet-class>net.biancheng.www.MyServletservlet-class>
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>MyServletservlet-name>
        <url-pattern>/myServleturl-pattern>
        <url-pattern>/myServlet3url-pattern>
    servlet-mapping>
web-app>

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/myServlet”,访问 MyServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第32张图片

在地址栏中输入“http://localhost:8080/servletDemo/myServlet3”,也可以访问 MyServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第33张图片

3. @WebServlet 实现多重映射

Servlet 3.0 增加了对 @WebServlet 注解的支持,我们可以在 urlPatterns 属性中,以字符串数组的形式指定一组映射规则来实现 Servlet 的多重映射。

以 servletDemo 为例,在 @WebServlet 注解的 urlPatterns 属性中添加一组虚拟路径,代码如下。

package net.biancheng.www;
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(
        urlPatterns = { "/myServlet", "/myServlet4" })
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private int initCount = 0;
    private int httpCount = 0;
    private int destoryCount = 0;
    @Override
    public void destroy() {
        destoryCount++;
        super.destroy();
        // 向控制台输出destory方法被调用次数
        System.out.println(
                "**********************************destroy方法:" + destoryCount + "*******************************");
    }
    @Override
    public void init() throws ServletException {
        initCount++;
        super.init();
        // 向控制台输出init方法被调用次数
        System.out.println("init方法:" + initCount);
    }
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        httpCount++;
        // 控制台输出doGet方法次数
        System.out.println("doGet方法:" + httpCount);
        // 设置返回页面格式与字符集
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        // 向页面输出
        writer.write("初始化次数:" + initCount + "
"
+ "处理请求次数:" + httpCount + "
"
+ "销毁次数:" + destoryCount); writer.close(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/myServlet”,访问 MyServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第34张图片

在地址栏中输入“http://localhost:8080/servletDemo/myServlet4”,也可以访问 MyServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第35张图片

Servlet虚拟路径匹配规则

当 Servlet 容器接收到请求后,容器会将请求的 URL 减去当前应用的上下文路径,使用剩余的字符串作为映射 URL 与 Servelt 虚拟路径进行匹配,匹配成功后将请求交给相应的 Servlet 进行处理。

以 servletDemo 为例,若 URL 为“http://localhost:8080/servletDemo/myServlet”,其应用上下文是 servletDemo,容器会将“http://localhost:8080/servletDemo”去掉,使用剩余的“/myServlet”与 Servlet 虚拟路径进行匹配。

匹配规则

Servlet 虚拟路径匹配规则有以下 4 种:

  1. 完全路径匹配
  2. 目录匹配
  3. 扩展名匹配
  4. 缺省匹配(默认匹配)

下面我们以 servletDemo 为例,分别介绍 4 种规则。

匹配规则 使用规则 虚拟路径 可访问的URL
完全路径匹配 (精确匹配) /开始,不能包含通配符*。 必须完全匹配 /myServlet /user/myServlet /product/index.action http://localhost:8080/servletDemo/myServlet http://localhost:8080/servletDemo/user/myServlet http://localhost:8080/servletDemo/product/index.action
目录匹配 /字符开头,并以/*结尾的字符串。 用于路径匹配 /user/* /* http://localhost:8080/servletDemo/user/aaa http://localhost:8080/servletDemo/aa
扩展名匹配 以通配符*.开头的字符串。 用于扩展名匹配 *.do *.action *.jsp http://localhost:8080/servletDemo/user.do http://localhost:8080/servletDemo/myServlet.action http://localhost:8080/servletDemo/bb.jsp
缺省匹配(默认匹配) 映射路径为/,表示这个 Servlet 为当前应用的缺省 Servlet 或默认 Servlet,默认处理无法匹配到虚拟路径的请求。 / 可以匹配任意请求 URL

注意:目录匹配和扩展名匹配无法混合使用,即/rest/*.do这种写法是不正确的。

匹配优先级

Servlet 虚拟路径的匹配优先级顺序为:全路径匹配(精确匹配)> 目录匹配 > 扩展名匹配 > 缺省匹配(默认匹配)。

Servlet 容器会从优先级高的虚拟路径开始匹配,匹配成功后就会立刻将请求交给相应的 Servlet 进行处理,不会再关注其他虚拟路径是否匹配成功。

示例 1

下面我们通过一个实例加深对 Servlet 虚拟路径匹配的理解。

在 servletDemo 的 net.biancheng.www 包下,创建名称为 VirtualPathServlet 的 Servlet 类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class VirtualPathServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        // 向页面输出
        writer.write("本次访问的Servlet是:" + this.getServletName());
        writer.close();
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

web.xml 的配置如下。


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    id="WebApp_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServlet1servlet-name>
        <servlet-class>net.biancheng.www.VirtualPathServletservlet-class>
    servlet>
    <servlet>
        <servlet-name>MyServlet2servlet-name>
        <servlet-class>net.biancheng.www.VirtualPathServletservlet-class>
    servlet>
    <servlet>
        <servlet-name>MyServlet3servlet-name>
        <servlet-class>net.biancheng.www.VirtualPathServletservlet-class>
    servlet>
    <servlet>
        <servlet-name>MyServlet4servlet-name>
        <servlet-class>net.biancheng.www.VirtualPathServletservlet-class>
    servlet>
    <servlet>
        <servlet-name>MyServlet5servlet-name>
        <servlet-class>net.biancheng.www.VirtualPathServletservlet-class>
    servlet>
    
    <servlet-mapping>
        <servlet-name>MyServlet1servlet-name>
        <url-pattern>/hellourl-pattern>
    servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>MyServlet2servlet-name>
        <url-pattern>/abc/my/*url-pattern>
    servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>MyServlet3servlet-name>
        <url-pattern>/abc/*url-pattern>
    servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>MyServlet4servlet-name>
        <url-pattern>*.dourl-pattern>
    servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>MyServlet5servlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>
web-app>

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/hello”,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第36张图片

在地址栏中输入“http://localhost:8080/servletDemo/abc/my/login”,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第37张图片

在地址栏中输入“http://localhost:8080/servletDemo/abc/index.do”,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第38张图片

在地址栏中输入“http://localhost:8080/servletDemo/hello/index.do”,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第39张图片

在地址栏中输入“http://localhost:8080/servletDemo/hello/pay.html”,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第40张图片

Tomcat 中的缺省 Servlet

在 Tomcat 安装目录的 \conf\web.xml 文件中,注册了一个名称为 org.apache.catalina.servlets.DefaultServlet 的 Servlet,并将它设置为缺省 Servlet。

    <servlet>
        <servlet-name>defaultservlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServletservlet-class>
        <init-param>
            <param-name>debugparam-name>
            <param-value>0param-value>
        init-param>
        <init-param>
            <param-name>listingsparam-name>
            <param-value>falseparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>defaultservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

Tomcat 服务器中的 Web 应用没有缺省 Servlet 时,会将 DefaultServlet 作为其缺省 Servlet。当客户端访问 Tomcat 服务器中某个静态 HTML 文件或者图片时,DefaultServlet 会判断该 HTML 或图片是否存在,若存在,则将数据以流的形式返回客户端,否则会报告 404 错误。

示例 2

将 servletDemo 应用的缺省匹配删除,并在 servletDemo 应用里添加一张图片 a.jpg。


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    id="WebApp_ID" metadata-complete="false" version="4.0">
    
    
    

web-app>

启动 Tomcat,在地址栏输入“http://localhost:8080/servletDemo/a.jpg”,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第41张图片

ServletConfig接口详解

Servlet 容器初始化 Servlet 时,会为这个 Servlet 创建一个 ServletConfig 对象,并将 ServletConfig 对象作为参数传递给 Servlet 。通过 ServletConfig 对象即可获得当前 Servlet 的初始化参数信息。

一个 Web 应用中可以存在多个 ServletConfig 对象,一个 Servlet 只能对应一个 ServletConfig 对象,即 Servlet 的初始化参数仅对当前 Servlet 有效。

获得 ServletConfig 对象

获得 ServletConfig 对象一般有 2 种方式:

1. 直接从带参的 init() 方法中提取
public class ServletConfigDemo extends HttpServlet {
    private ServletConfig servletConfig;
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取Servlet得名字
        this.servletConfig.getServletName();
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        //从带参init方法中,提取ServletConfig对象
        this.servletConfig = config;
    }
}
2. 调用 GenericServlet 提供的 getServletConfig() 方法获得
//调用 GenericServlet 提供的 getServletConfig 方法获得 ServletConfig 对象ServletConfig servletConfig = this.getServletConfig();

ServletConfig 接口

javax.servlet 包提供了一个 ServletConfig 接口,该接口中提供了以下方法。

返回值类型 方法 功能描述
String getInitParameter(String name) 根据初始化参数名 name,返回对应的初始化参数值。
Enumeration getInitParameterNames() 返回 Servlet 所有的初始化参数名的枚举集合,如果该 Servlet 没有初始化参数,则返回一个空的集合。
ServletContext getServletContext() 返回一个代表当前 Web 应用的 ServletContext 对象。
String getServletName() 返回 Servlet 的名字,即 web.xml 中 元素的值。

配置 Servlet 初始化参数

配置 Servlet 的初始化参数有 2 种方式:

  1. 使用 web.xml 配置初始化参数;
  2. 使用 @WebServlet 配置初始化参数。
1.使用 web.xml 配置初始化参数

在 web.xml 中可以使用一个或多个 元素为 Servlet 配置初始化参数,代码如下。


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    id="WebApp_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServletservlet-name>
        <servlet-class>net.biancheng.www.MyServletservlet-class>
        
        <init-param>
            <param-name>nameparam-name>
            <param-value>编程帮param-value>
        init-param>
        
        <init-param>
            <param-name>URLparam-name>
            <param-value>www.biancheng.netparam-value>
        init-param>
    servlet>
web-app>

以上配置说明如下:

  • 元素是 的子元素, 需要在 元素内使用,表示只对当前 Servlet 有效 。
  • 子元素表示参数的名称。
  • 子元素表示参数的值。
2.使用 @WebServlet 配置初始化参数

通过 @WebServlet 的 initParams 属性也可以为 Servlet 设置初始化参数,代码如下。

package net.biancheng.www;
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;
import java.io.IOException;
@WebServlet(urlPatterns = {"/MyServlet"}, initParams = {@WebInitParam(name = "name", value = "编程帮"),
        @WebInitParam(name = "URL", value = "www.biancheng.net")})
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

获取 Servlet 初始化参数

下面我们通过一个例子演示如何通过 ServletConfig 对象读取 Servlet 的初始化参数。

以 servletDemo 工程为例,在 net.biancheng.www 包下,创建名称为 ReadConfigServlet 的类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
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;
/**
*
* @author 编程帮 www.biancheng.net
* 获取Servlet的初始化参数
*
*/
@WebServlet(urlPatterns = { "/ReadConfigServlet" }, initParams = { @WebInitParam(name = "name", value = "编程帮"),
        @WebInitParam(name = "URL", value = "www.biancheng.net") })
public class ReadConfigServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        // 获取ServletConfig对象
        ServletConfig config = getServletConfig();
        // 获取servletName
        String servletName = config.getServletName();
        // 返回 servlet 的初始化参数的名称的集合
        Enumeration<String> initParameterNames = config.getInitParameterNames();
        // 遍历集合获取初始化参数名称
        while (initParameterNames.hasMoreElements()) {
            // 获取初始化参数名称
            String initParamName = initParameterNames.nextElement();
            // 获取相应的初始参数的值
            String initParamValue = config.getInitParameter(initParamName);
            // 向页面输出
            writer.write(initParamName + "  :  " + initParamValue + "
"
); } // 关闭流 writer.close(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

启动 Tomcat 服务器,在地址栏输入“http://localhost:8080/servletDemo/ReadConfigServlet”,访问 ReadConfigServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第42张图片

ServletContext接口详解

Servlet 容器启动时,会为每个 Web 应用(webapps 下的每个目录都是一个 Web 应用)创建一个唯一的 ServletContext 对象,该对象一般被称为“Servlet 上下文”。

ServletContext 对象的生命周期从 Servlet 容器启动时开始,到容器关闭或应用被卸载时结束。

Web 应用中的所有 Servlet 共享同一个 ServletContext 对象,不同 Servlet 之间可以通过 ServletContext 对象实现数据通讯,因此 ServletContext 对象也被称为 Context 域对象。

域对象是服务器在内存上创建的存储空间,该空间用于不同动态资源(例如 Servlet、JSP)之间传递与共享数据。

获得 ServletContext 对象

获得 ServletContext 对象有以下 4 种方式:

1. 通过 GenericServlet 提供的 getServletContext() 方法
//通过 GenericServlet的getServletContext方法获取ServletContext对象ServletContext servletContext = this.getServletContext();
2. 通过 ServletConfig 提供的 getServletContext() 方法
//通过 ServletConfig的 getServletContext方法获取ServletContext对象ServletContext servletContext = this.getServletConfig().getServletContext();
3. 通过 HttpSession 提供的 getServletContext() 方法
//通过 HttpSession的 getServletContext方法获取ServletContext对象ServletContext servletContext = req.getSession().getServletContext();
4. 通过 HttpServletRequest 提供的 getServletContext() 方法
//通过 HttpServletRequest的 getServletContext方法获取ServletContext对象ServletContext servletContext = req.getServletContext();

注意:以上最后两种方法了解即可,后面我们会详细讲解。

ServletContext 的应用

javax.servlet 包提供了一个 ServletContext 接口,该接口定义了一组方法,Servlet 可以使用这些方法与容器进行通信。

ServletContext 的应用主要有以下 3 个:

  1. 获取上下文初始化参数
  2. 实现 Servlet 之间的数据通讯
  3. 读取 Web 应用下的资源文件

1. 获取上下文初始化参数

使用 ServletContext 对象获取 Web 应用的上下文初始化参数,分为 2 步:

  1. 设置上下文初始化参数
  2. 调用接口中方法获取初始化参数
1) 设置上下文初始化参数

通过 web.xml 中的 元素可以为 Web 应用设置一些全局的初始化参数,这些参数被称为上下文初始化参数。

与 Servlet 的初始化参数不同,应用中的所有 Servlet 都共享同一个上下文初始化参数。在 Web 应用的整个生命周期中,上下文初始化参数会一直存在,并且可以随时被任意一个 Servlet 访问。

在 web.xml 文件中配置上下文初始化参数,代码如下所示。


<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0" metadata-complete="false">
    
    <context-param>
        <param-name>nameparam-name>
        <param-value>编程帮param-value>
    context-param>
    <context-param>
        <param-name>urlparam-name>
        <param-value>www.biancheng.netparam-value>
    context-param>
web-app>

对以上标签说明如下:

  • 元素用来声明上下文初始化参数,必须在根元素 内使用。
  • 子元素表示参数名,参数名在整个 Web 应用中必须是唯一的。
  • 子元素表示参数值。
2) 调用接口中方法获取初始化参数

Servlet 容器启动时,会为容器内每个 Web 应用创建一个 ServletContext 对象,并将 元素中的上下文初始化参数以键值对的形式存入该对象中,因此我们可以通过 ServletContext 的相关方法获取到这些初始化参数。

下表列举了 ServletContext 接口中用于获取上下文初始化参数的相关方法。

返回值类型 方法 描述
String getInitParameter(String name) 根据初始化参数名 name,返回对应的初始化参数值。
Enumeration getInitParameterNames() 返回 Web 应用所有上下文初始化参数名的枚举集合,如果该 Web 应用没有上下文初始化参数,则返回一个空的枚举集合。
例 1

以 servletDemo 为例,在 net.biancheng.www 包下创建一个名称为 ReadContextServlet 的类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/ReadContextServlet")
public class ReadContextServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        // 调用httpServlet父类GenericServlet的getServletContext方法获取ServletContext对象
        ServletContext context = super.getServletContext();
        // 返回 context 上下文初始化参数的名称
        Enumeration<String> initParameterNames = context.getInitParameterNames();
        while (initParameterNames.hasMoreElements()) {
            // 获取初始化参数名称
            String initParamName = initParameterNames.nextElement();
            // 获取相应的初始参数的值
            String initParamValue = context.getInitParameter(initParamName);
            // 向页面输出
            writer.write(initParamName + "  :  " + initParamValue + "
"
); } // 关闭流 writer.close(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

启动 Tomcat 服务器,在地址栏输入“http://localhost:8080/servletDemo/ReadContextServlet”,访问 ReadContextServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第43张图片

2. 实现数据通讯

在 Servlet 中,调用 ServletContext 接口的 setAttribute() 方法可以创建一些属性,这些属性被存放在 ServletContext 对象中。应用中所有 Servlet 都可以对这些属性进行访问和操作,通过它们可以实现应用内不同 Servlet 之间的数据通讯。

数据通讯的相关方法

下表列举了 ServletContext 接口实现数据通讯的相关方法。

返回值类型 方法 描述
void setAttribute(String name, Object object) 把一个 Java 对象与一个属性名绑定,并将它作为一个属性存放到 ServletContext 中。 参数 name 为属性名,参数 object 为属性值。
void removeAttribute(String name) 从 ServletContext 中移除属性名为 name 的属性。
Object getAttribute(String name) 根据指定的属性名 name,返回 ServletContext 中对应的属性值。
ServletContext 属性与上下文初始化参数对比

虽然 ServletContext 的属性与上下文初始化参数都是存放在 ServletContext 对象中,但它们是不同的。

不同点 ServletContext 的属性 上下文初始化参数
创建方式 ServletContext 的属性通过调用 ServletContext 接口的 setAttribute() 方法创建 上下文初始化参数通过 web.xml 使用 元素配置
可进行的操作 ServletContext 的属性可以通过 ServletContext 接口的方法进行读取、新增、修改、移除等操作 上下文初始化参数在容器启动后只能被读取,不能进行新增、修改和移除操作
生命周期 ServletContext 中属性的生命周期从创建开始,到该属性被移除(remove)或者容器关闭结束 上下文初始化参数的生命周期,从容器启动开始,到 Web 应用被卸载或容器关闭结束
作用 使用 ServletContext 中的属性可以实现 Servlet 之间的数据通讯 使用上下文初始化参数无法实现数据通讯
例 2

我们通过编写一个统计页面访问量的案例,来演示如何通过 ServletContext 对象实现数据通讯。

在 servletDemo 的 net.biancheng.www 包下,创建一个名称为 CountServlet 的 Servlet 类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author 编程帮 www.biancheng.net
* 使用ServletContext 统计访问次数
*
*/
@WebServlet("/CountServlet")
public class CountServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public void init() throws ServletException {
        // 获取ServletContext对象
        ServletContext context = getServletContext();
        // 初始化时,向ServletContext中设置count属性,初始值为0
        context.setAttribute("count", 0);
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 调用httpServlet父类GenericServlet的getServletContext方法获取ServletContext对象
        ServletContext context = super.getServletContext();
        // 获取count的值,自增
        Integer count = (Integer) context.getAttribute("count");
        // 存入到域对象中
        context.setAttribute("count", ++count);
        // 向页面输出内容
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("

编程帮 www.biancheng.net 欢迎您

"
); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

然后再创建一个名称为 ShowServlet 的 Servlet 类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author 编程帮 www.biancheng.net
* 使用ServletContext展示网站的访问次数
*
*/
@WebServlet("/ShowServlet")
public class ShowServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获取ServletContext中存放的count属性(即页面的访问次数)
        Integer count = (Integer) getServletContext().getAttribute("count");
        // 向页面输出
        response.setContentType("text/html;charset=UTF-8");
        // 若CountServlet已被访问
        if (count != null) {
            response.getWriter().write("

该网站一共被访问了" + count + "次

"
); } else { // 若CountServlet未被访问,提示先访问CountServlet response.getWriter().write("

请先访问 CountServlet

"
); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

重启 Tomcat 浏览器,在地址栏输入“http://localhost:8080/servletDemo/CountServlet”,多次访问 CountServlet 次,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第44张图片

然后再输入“http://localhost:8080/servletDemo/ShowServlet”,访问 ShowServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第45张图片

3. 读取 Web 应用下的资源文件

在实际开发中,有时会需要读取 Web 应用中的一些资源文件,如配置文件和日志文件等。为此,ServletContext 接口定义了一些读取 Web 资源的方法 ,如下表。

返回值类型 方法 方法描述
Set getResourcePaths(String path) 返回一个 Set 集合,该集合中包含资源目录中的子目录和文件的名称。
String getRealPath(String path) 返回资源文件的真实路径(文件的绝对路径)。
URL getResource(String path) 返回映射到资源文件的 URL 对象。
InputStream getResourceAsStream(String path) 返回映射到资源文件的 InputStream 输入流对象。

注:上表中参数 path 代表资源文件的虚拟路径,它以正斜线/开始,/表示当前 Web 应用的根目录。

例 3

下面我们通过一个例子演示如何使用 ServletContext 对象读取资源文件。

在 servletDemo 的 src 目录中,创建一个名称为 db.properties 的文件,文件中输入如下所示的配置信息。

name=编程帮
url=www.biancheng.net
desc=编程帮,欢迎你

在 net.biancheng.www 包中,创建一个名称为 ReadServlet 的 Servlet 类,代码如下所示。

package net.biancheng.www;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author 编程帮 www.biancheng.net
* 使用ServletContext肚读取资源文件
*
*/
@WebServlet("/ReadServlet")
public class ReadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        // 获取相对路径中的输入流对象
        InputStream ins = getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
        // 获取输入流
        Properties pro = new Properties();
        // 加载
        pro.load(ins);
        // 获取文件中的内容
        String name = pro.getProperty("name");
        String url = pro.getProperty("url");
        String desc = pro.getProperty("desc");
        writer.write("用户名:" + name + "
"
+ "地址:" + url + "
"
+ "描述:" + desc + "
"
); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/ReadServlet”,访问 ReadServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第46张图片

HttpServletRequest接口详解

一般情况下,浏览器(客户端)通过 HTTP 协议来访问服务器的资源,Servlet 主要用来处理 HTTP 请求。

Servlet 处理 HTTP 请求的流程如下:

【Java学习路线之JavaWeb】Servlet教程_第47张图片

  1. Servlet 容器接收到来自客户端的 HTTP 请求后,容器会针对该请求分别创建一个 HttpServletRequest 对象和 HttpServletReponse 对象。
  2. 容器将 HttpServletRequest 对象和 HttpServletReponse 对象以参数的形式传入 service() 方法内,并调用该方法。
  3. 在 service() 方法中 Servlet 通过 HttpServletRequest 对象获取客户端信息以及请求的相关信息。
  4. 对 HTTP 请求进行处理。
  5. 请求处理完成后,将响应信息封装到 HttpServletReponse 对象中。
  6. Servlet 容器将响应信息返回给客户端。
  7. 当 Servlet 容器将响应信息返回给客户端后,HttpServletRequest 对象和 HttpServletReponse 对象被销毁。

通过以上流程可以看出, HttpServletRequest 和 HttpServletReponse 是 Servlet 处理 HTTP 请求流程中最重要的两个对象。HttpServletRequest 对象用于封装 HTTP 请求信息,HttpServletReponse 对象用于封装 HTTP 响应信息。

下面我们将对 HttpServletRequest 进行详细介绍。

HttpServletRequest 接口

在 Servlet API 中,定义了一个 HttpServletRequest 接口,它继承自 ServletRequest 接口。HttpServletRequest 对象专门用于封装 HTTP 请求消息,简称 request 对象。

HTTP 请求消息分为请求行、请求消息头和请求消息体三部分,所以 HttpServletRequest 接口中定义了获取请求行、请求头和请求消息体的相关方法。

获取请求行信息

HTTP 请求的请求行中包含请求方法、请求资源名、请求路径等信息,HttpServletRequest 接口定义了一系列获取请求行信息的方法,如下表。

返回值类型 方法声明 描述
String getMethod() 该方法用于获取 HTTP 请求方式(如 GET、POST 等)。
String getRequestURI() 该方法用于获取请求行中的资源名称部分,即位于 URL 的主机和端口之后,参数部分之前的部分。
String getQueryString() 该方法用于获取请求行中的参数部分,也就是 URL 中“?”以后的所有内容。
String getContextPath() 返回当前 Servlet 所在的应用的名字(上下文)。对于默认(ROOT)上下文中的 Servlet,此方法返回空字符串""。
String getServletPath() 该方法用于获取 Servlet 所映射的路径。
String getRemoteAddr() 该方法用于获取客户端的 IP 地址。
String getRemoteHost() 该方法用于获取客户端的完整主机名,如果无法解析出客户机的完整主机名,则该方法将会返回客户端的 IP 地址。
示例 1

为了更好地理解这些方法,下面通过案例演示这些方法的使用。

创建一个名称为 httpServletRequestDemo 的项目,在 net.biancheng.www 包中创建一个名为 RequestLine 的 Servlet 类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/RequestLine")
public class RequestLine extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        writer.println("请求方式:" + request.getMethod() + "
"
+ "客户端的 IP 地址:" + request.getRemoteAddr() + "
"
+ "应用名字(上下文):" + request.getContextPath() + "
"
+ "URI:" + request.getRequestURI() + "
"
+ "请求字符串:" + request.getQueryString() + "
"
+ "Servlet所映射的路径:" + request.getServletPath() + "
"
+ "客户端的完整主机名:" + request.getRemoteHost() + "
"
); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/httpServletRequestDemo/RequestLine?a=1&b=2”,访问 RequestLine,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第48张图片

获取请求头信息

当浏览器发送请求时,需要通过请求头向服务器传递一些附加信息,例如客户端可以接收的数据类型、压缩方式、语言等。为了获取请求头中的信息, HttpServletRequest 接口定义了一系列用于获取 HTTP 请求头字段的方法,如下表所示。

返回值类型 方法声明 描述
String getHeader(String name) 该方法用于获取一个指定头字段的值。 如果请求消息中包含多个指定名称的头字段,则该方法返回其中第一个头字段的值。
Enumeration getHeaders(String name) 该方法返回指定头字段的所有值的枚举集合, 在多数情况下,一个头字段名在请求消息中只出现一次,但有时可能会出现多次。
Enumeration getHeaderNames() 该方法返回请求头中所有头字段的枚举集合。
String getContentType() 该方法用于获取 Content-Type 头字段的值。
int getContentLength() 该方法用于获取 Content-Length 头字段的值 。
String getCharacterEncoding() 该方法用于返回请求消息的字符集编码 。
示例 2

为了更好地理解这些方法,下面通过案例演示这些方法的使用。

在 net.biancheng.www 包中创建一个名为 RequestHeader 的 Servlet 类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/RequestHeader")
public class RequestHeader extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        //获得所有请求头字段的枚举集合
        Enumeration<String> headers = request.getHeaderNames();
        while (headers.hasMoreElements()) {
            //获得请求头字段的值
            String value = request.getHeader(headers.nextElement());
            writer.write(headers.nextElement() + ":" + value + "
"
); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/httpServletRequestDemo/RequestHeader”,访问 RequestHeader ,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第49张图片

获取 form 表单的数据

在实际开发中,我们经常需要获取用户提交的表单数据,例如用户名和密码等。为了方便获取表单中的请求参数,ServletRequest 定义了一系列获取请求参数的方法,如下表所示。

返回值类型 方法声明 功能描述
String getParameter(String name) 返回指定参数名的参数值。
String [ ] getParameterValues (String name) 以字符串数组的形式返回指定参数名的所有参数值(HTTP 请求中可以有多个相同参数名的参数)。
Enumeration getParameterNames() 以枚举集合的形式返回请求中所有参数名。
Map getParameterMap() 用于将请求中的所有参数名和参数值装入一个 Map 对象中返回。
示例 3

为了更好地理解这些方法,下面通过案例演示这些方法的使用。

在 httpServletRequestDemo 的 WebContent 目录下,创建 form.html,代码如下。


<html>
<head>
<meta charset="UTF-8">
<title>Insert title heretitle>
head>
<body>
    <form action="/httpServletRequestDemo/RequestParam" method="post">
        <table border="1" width="50%">
            <tr>
                <td colspan="2" align="center">编程帮wwww.biancheng.nettd>
            tr>
            <tr>
                <td>输入姓名td>
                <td><input type="text" name="username" />td>
            tr>
            <tr>
                <td>输入密码td>
                <td><input type="password" name="password" />td>
            tr>
            <tr>
                <td>选择性别td>
                <td><input type="radio" name="sex" value="male" /><input
                    type="radio" name="sex" value="female" />td>
            tr>
            <tr>
                <td>选择使用的语言td>
                <td><input type="checkbox" name="language" value="JAVA" />JAVA
                    <input type="checkbox" name="language" value="C" />C语言 <input
                    type="checkbox" name="language" value="PHP" />PHP <input
                    type="checkbox" name="language" value="Python" />Pythontd>
            tr>
            <tr>
                <td>选择城市td>
                <td><select name="city">
                        <option value="none">--请选择--option>
                        <option value="beijing">北京option>
                        <option value="shanghai">上海option>
                        <option value="guangzhou">广州option>
                select>td>
            tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交" />td>
            tr>
        table>
    form>
body>
html>

在 net.biancheng.www 包中,创建一个名为 RequestParam 的 Servlet 类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/RequestParam")
public class RequestParam extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        // 获取内容,做其他操作
        // 获取姓名
        String username = request.getParameter("username");
        // 获取密码
        String password = request.getParameter("password");
        // 获取性别
        String sex = request.getParameter("sex");
        // 获取城市
        String city = request.getParameter("city");
        // 获取语言
        String[] languages = request.getParameterValues("language");
        writer.write("用户名:" + username + "
"
+ "密码:" + password + "
"
+ "性别:" + sex + "
"
+ "城市:" + city + "
"
+ "使用过的语言:" + Arrays.toString(languages) + "
"
); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/httpServletRequestDemo/form.html”,即可访问 form.html,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第50张图片

在表单中填写信息后,点击提交,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第51张图片

中文乱码问题

如果在 form 表单输入框中输入中文,例如在输入姓名时,填写“编程帮默认用户”,点击提交后就会出现乱码问题,如下图所示。

【Java学习路线之JavaWeb】Servlet教程_第52张图片

根据请求方式的不同,请求一般可以被分为两种:GET 请求和 POST 请求。这两种请求方式都可能会产生中文乱码问题,下面我们分别对它们产生乱码的原因及其解决方案进行介绍。

POST 请求

乱码的原因:POST 提交的数据在请求体中,其所使用的编码格式时页面一致(即 utf-8)。request 对象接收到数据之后,会将数据放到 request 缓冲区,缓冲区的默认字符集是 ISO-8859-1(该字符集不支持中文),两者使用的字符集不一致导致乱码。

解决方案:在获取请求参数之前设置 request 缓冲区字符集为 utf-8 ,代码如下。

//修改request缓冲区的字符集为UTF-8
request.setCharacterEncoding("utf-8");
// 获取用户名
String username = request.getParameter("username");
GET 请求

乱码的原因:Get 请求将请求数据附加到 URL 后面作为参数,浏览器发送文字时采用的编码格式与页面编码保持一致(utf-8)。如果 Tomcat 没有设置字符集,接收 URL 时默认使用 ISO-8859-1 进行解码,ISO-8859-1 不兼容中文,无法正确解码,导致出现乱码。

需要注意的是,在 Tomcat 8 中已解决了 get 方式提交请求中文乱码的问题,使用 Tomcat 8 及以上版本的同学不必再考虑此问题了,如果您使用的是 Tomcat 7 或更早的版本,出现乱码问题可以使用如下的方案解决。

解决方案:解决 GET 请求中文乱码问题,有以下 3 种解决方案。

\1. 修改 tomcat/conf/server.xml 中的配置,代码如下。


\2. 使用 URLEncoder 和 URLDecoder 进行编码和解码的操作(逆向编解码)。

//得到TOMCAT通过ISO8859-1解码的字符串
String username = request.getParameter("username");
//对字符串使用ISO8859-1进行编码,得到最初浏览器使用UTF-8编码的字符串
username = URLEncoder.encode(username, "ISO8859-1");
//将使用UTF-8编码的字符串使用UTF-8进行解码,得到正确的字符串
username = URLDecoder.decode(username, "UTF-8");

\3. 使用 String 的构造方法:String(byte[] bytes, String charset) ,对字节数组(bytes)按照指定的字符集(charset)进行解码,返回解码后的字符串,解决乱码问题(推荐使用)。

 //获取姓名
String username = request.getParameter("username");
//使用String的构造方法解决乱码的问题
username = new String(username.getBytes("ISO-8859-1"),"UTF-8");

Servlet请求转发

Web 应用在处理客户端的请求时,经常需要多个 Web 资源共同协作才能生成响应结果。但由于 Serlvet 对象无法直接调用其他 Servlet 的 service() 方法,所以 Servlet 规范提供了 2 种解决方案:

  1. 请求转发
  2. 请求包含(了解即可)

下面我们主要对请求转发进行介绍。

请求转发

请求转发属于服务器行为。容器接收请求后,Servlet 会先对请求做一些预处理,然后将请求传递给其他 Web 资源,来完成包括生成响应在内的后续工作。

RequestDispatcher 接口

javax.servlet 包中定义了一个 RequestDispatcher 接口,RequestDispatcher 对象由 Servlet 容器创建,用于封装由路径所标识的 Web 资源。利用 RequestDispatcher 对象可以把请求转发给其他的 Web 资源。

Servlet 可以通过 2 种方式获得 RequestDispatcher 对象:

  1. 调用 ServletContext 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,必须为绝对路径;
  2. 调用 ServletRequest 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,可以为绝对路径,也可以为相对路径。

绝对路径是指以符号“/”开头的路径,“/”表示当前 Web 应用的根目录。相对路径是指相对当前 Web 资源的路径,不以符号“/”开头。

RequestDispatcher 接口中提供了以下方法。

返回值类型 方法 功能描述
void forward(ServletRequest request,ServletResponse response) 用于将请求转发给另一个 Web 资源。该方法必须在响应提交给客户端之前被调用,否则将抛出 IllegalStateException 异常
void include(ServletRequest request,ServletResponse response) 用于将其他的资源作为当前响应内容包含进来
请求转发的工作原理

在 Servlet 中,通常使用 forward() 方法将当前请求转发给其他的 Web 资源进行处理。请求转发的工作原理如下图所示。

【Java学习路线之JavaWeb】Servlet教程_第53张图片

请求转发的特点

请求转发具有以下特点:

  1. 请求转发不支持跨域访问,只能跳转到当前应用中的资源。
  2. 请求转发之后,浏览器地址栏中的 URL 不会发生变化,因此浏览器不知道在服务器内部发生了转发行为,更无法得知转发的次数。
  3. 参与请求转发的 Web 资源之间共享同一 request 对象和 response 对象。
  4. 由于 forward() 方法会先清空 response 缓冲区,因此只有转发到最后一个 Web 资源时,生成的响应才会被发送到客户端。

request 域对象

request 是 Servlet 的三大域对象之一,它需要与请求转发配合使用,才可以实现动态资源间的数据传递。

在 ServletRequest 接口中定义了一系列操作属性的方法,如下表。

返回值类型 方法 描述
void setAttribute(String name, Object o) 将 Java 对象与属性名绑定,并将它作为一个属性存放到 request 对象中。参数 name 为属性名,参数 object 为属性值。
Object getAttribute(String name) 根据属性名 name,返回 request 中对应的属性值。
void removeAttribute(String name) 用于移除 request 对象中指定的属性。
Enumeration getAttributeNames() 用于返回 request 对象中的所有属性名的枚举集合。

Context 域对象和 request 域对象对比,具有以下 4 点差异:

1) 生命周期不同

Context 域对象的生命周期从容器启动开始,到容器关闭或者 Web 应用被移除时结束;

request 域对象的生命周期从客户端向容器发送请求开始,到对这次请求做出响应后结束。

2) 作用域不同

Context 域对象对整个 Web 应用内的所有Servlet都有效;

request 域对象只对本次请求涉及的 Servlet 有效。

3) Web 应用中数量不同

整个 Web 应用中只有一个 Context 域对象;

由于 Servlet 能处理多个请求,因此 Web 应用中的每个 Servlet 实例都可以有多个 request 域对象。

4) 实现数据共享的方式不同

Context 域对象可以独立完成动态资源之间的数据共享;

Request 域对象需要与请求转发配合使用才能实现动态资源之间的数据共享。

示例

下面我们通过一个案例加深大家对转发和 request 域对象的理解。

在 httpServletRequestDemo 的 net.biancheng.www 包中,创建一个名为 DispatcherServlet 的类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author 编程帮  www.biancheng.net
*请求转发
*/
@WebServlet("/DispatcherServlet")
public class DispatcherServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 设置向页面输出内容格式
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        // 尝试在请求转发前向response缓冲区写入内容,最后在页面查看是否展示
        writer.write("

这是转发前在响应信息内的内容!

"
); // 向reuqest域对象中添加属性,传递给下一个web资源 request.setAttribute("webName", "C语言中文网"); request.setAttribute("url", "www.biancheng.net"); request.setAttribute("welcome", "C语言中文网,欢迎你"); // 转发 request.getRequestDispatcher("/DoServlet").forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

然后,再创建一个名称为 DoServlet 的类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author 编程帮 www.biancheng.net
* 请求转发
*
*/
@WebServlet("/DoServlet")
public class DoServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 设置向页面输出内容格式
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        String webName = (String) request.getAttribute("webName");
        String url = (String) request.getAttribute("url");
        String welcome = (String) request.getAttribute("welcome");
        if (webName != null) {
            writer.write("

" + webName + "

"
); } if (url != null) { writer.write("

" + url + "

"
); } if (welcome != null) { writer.write("

" + welcome + "

"
); } String username = request.getParameter("username"); // 获取密码 String password = request.getParameter("password"); // 获取性别 String sex = request.getParameter("sex"); // 获取城市 String city = request.getParameter("city"); // 获取使用语言返回是String数组 String[] languages = request.getParameterValues("language"); writer.write("用户名:" + username + "
"
+ "密码:" + password + "
"
+ "性别:" + sex + "
"
+ "城市:" + city + "
"
+ "使用过的语言:" + Arrays.toString(languages) + "
"
); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

在 WebContent 根目录下,创建 login.html,代码如下。


<html>
<head>
<meta charset="UTF-8">
<title>Insert title heretitle>
head>
<body>
    <form action="/httpServletRequestDemo/DispatcherServlet" method="GET">
        <table border="1" width="50%">
            <tr>
                <td colspan="2" align="center">编程帮wwww.biancheng.nettd>
            tr>
            <tr>
                <td>输入姓名td>
                <td><input type="text" name="username" />td>
            tr>
            <tr>
                <td>输入密码td>
                <td><input type="password" name="password" />td>
            tr>
            <tr>
                <td>选择性别td>
                <td><input type="radio" name="sex" value="" /><input
                    type="radio" name="sex" value="" />td>
            tr>
            <tr>
                <td>选择使用的语言td>
                <td><input type="checkbox" name="language" value="JAVA" />JAVA
                    <input type="checkbox" name="language" value="C语言" />C语言 <input
                    type="checkbox" name="language" value="PHP" />PHP <input
                    type="checkbox" name="language" value="Python" />Pythontd>
            tr>
            <tr>
                <td>选择城市td>
                <td><select name="city">
                        <option value="none">--请选择--option>
                        <option value="北京">北京option>
                        <option value="北京">上海option>
                        <option value="广州">广州option>
                select>td>
            tr>
            <tr>
                <td colspan="2" align="center"><input type="submit" value="提交" />
                td>
            tr>
        table>
    form>
body>
html>

启动 Tomcat 服务器,在地址栏输入“http://localhost:8080/httpServletRequestDemo/login.html”,访问 login.html,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第54张图片

填写表单信息,点击提交,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第55张图片

HttpServletResponse接口详解

在 Servlet API 中,定义了一个 HttpServletResponse 接口,它继承自 ServletResponse 接口。HttpServletResponse 对象专门用来封装 HTTP 响应消息,简称 response 对象。

Servlet 容器会针对每次请求创建一个 response 对象,并把它作为参数传递给 Servlet 的 service 方法。Servlet 处理请求后,会将响应信息封装到 response 对象中,并由容器解析后返回给客户端。

由于 HTTP 响应消息由响应行、响应头、消息体三部分组成,所以 HttpServletResponse 接口中定义了向客户端发送响应状态码、响应头、响应体的方法,下面我们将针对这些方法进行介绍。

响应行相关的方法

当 Servlet 返回响应消息时,需要在响应消息中设置状态码。因此,HttpServletResponse 接口定义了发送状态码的方法,如下表。

返回值类型 方法 描述
void setStatus(int status) 用于设置 HTTP 响应消息的状态码,并生成响应状态行。
void sendError(int sc) 用于发送表示错误信息的状态码。

响应头相关的方法

HttpServletResponse 接口中定义了一系列设置 HTTP 响应头字段的方法,如下表所示。

返回值类型 方法 描述
void addHeader(String name,String value) 用于增加响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值。
void setHeader (String name,String value) 用于设置响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值。
void addIntHeader(String name,int value) 用于增加值为 int 类型的响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值,类型为 int。
void setIntHeader(String name, int value) 用于设置值为 int 类型的响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值,类型为 int。
void setContentType(String type) 用于设置 Servlet 输出内容的 MIME 类型以及编码格式。
void setCharacterEncoding(String charset) 用于设置输出内容使用的字符编码。

响应体相关的方法

由于在 HTTP 响应消息中,大量的数据都是通过响应消息体传递的。因此 ServletResponse 遵循以 I/O 流传递大量数据的设计理念,在发送响应消息体时,定义了两个与输出流相关的方法。

返回值类型 方法 描述
ServletOutputStream getOutputStream() 用于获取字节输出流对象。
PrintWriter getWriter() 用于获取字符输出流对象。

注意:getOutputStream() 和 getWriter() 方法互相排斥,不可同时使用,否则会发生 IllegalStateException 异常。

示例

创建一个名称为 responseDemo 的项目,并在 net.biancheng.www 包下创建名称为 OutServlet 的 Servlet 类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author 编程帮 www.biancheng.net 使用response对象向页面输出内容
*
*/
@WebServlet("/OutServlet")
public class OutServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        run1(response);
        // run2(response);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
    // 使用字符流向页面输出
    public void run2(HttpServletResponse response) throws IOException {
        response.getWriter().write("编程帮 www.biancheng.net");
    }
    // 使用字节流向页面输出
    // 使用字节流向页面输出
    public void run1(HttpServletResponse response) throws IOException {
        // 设置浏览器打开文件时编码
        response.setHeader("Content-Type", "text/html;charset=UTF-8");
        // 获取字节输出流
        OutputStream os = response.getOutputStream();
        byte[] str = "编程帮 www.biancheng.net".getBytes("UTF-8");
        // 输出中文
        os.write(str);
    }
}

启动 Tomat 服务器,在客户端浏览器地址栏输入“http://localhost:8080/responseDemo/OutServlet”访问 OutServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第56张图片

在 doGet() 方法中调用 run2() 方法,使用字符流输出响应内容到浏览器,再次访问 OutServlet,结果如下图。

【Java学习路线之JavaWeb】Servlet教程_第57张图片

response 中文乱码问题

response 对象向页面输出时有两种方式:字节流、字符流,这两种方式输出中文时都有可能出现乱码。下面我们针对这两种方式出现乱码的原因以及解决方案进行介绍。

使用字节流输出中文

ServletOutputStream outptuStream = response.getOutputStream();
outputStream.write(“编程帮 www.biancheng.net”.getBytes());

使用字节流向页面输出中文是否会出现乱码问题?不一定。

乱码原因:

字节流输出中文是否出现乱码,取决于中文转成字节数组时与浏览器打开时采用的字符集是否一致。若两者保持一致,则不会出现乱码问题,若不一致就会出现乱码问题。

解决方案:

将中文转成字节数组时和浏览器默认采用的字符集保持一致即可,代码如下。

// 设置response缓冲区的编码
response.setCharacterEncoding("UTF-8");
// 设置浏览器打开文件所采用的编码
response.setHeader("Content-Type", "text/html;charset=UTF-8");
// 输出中文
response.getWriter().write("编程帮 www.biancheng.net");

使用字符流输出中文

使用字符流向页面输出中文是否会出现乱码问题?一定乱码。

乱码原因:

通过字符流输出的内容是存放在 response 缓冲区的,response 缓冲区的默认字符集是 ISO-8859-1,该字符集不支持中文。

解决方案:

将 response 缓冲区和浏览器采用的字符集保持一致即可,有如下 2 种的方式。

第一种方式:

// 设置response缓冲区的编码
response.setCharacterEncoding("UTF-8");
// 设置浏览器打开文件所采用的编码
response.setHeader("Content-Type", "text/html;charset=UTF-8");
// 输出中文
response.getWriter().write("编程帮 www.biancheng.net");

第二种方式:

response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("编程帮 www.biancheng.net");

你可能感兴趣的:(java,java,intellij-idea,tomcat)