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 的配置如下。
<?xml version="1.0" encoding="UTF-8"?>
MyServlet1
net.biancheng.www.MyServlet1
0
MyServlet2
net.biancheng.www.MyServlet1
1
MyServlet3
net.biancheng.www.MyServlet1
2
MyServlet4
net.biancheng.www.MyServlet1
-1
MyServlet5
net.biancheng.www.MyServlet1
MyServlet1
/MyServlet1
MyServlet2
/MyServlet2
MyServlet3
/MyServlet3
MyServlet4
/MyServlet4
MyServlet5
/MyServlet5
启动 Tomcat 服务器,控制台输出如下图所示。

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

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

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

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

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

由示例可知:
* 由于 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 单一映射,代码如下。
<?xml version="1.0" encoding="UTF-8"?>
MyServlet
net.biancheng.www.MyServlet
MyServlet
/myServlet
对以上标签说明如下:
* 元素用于注册 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 中配置两个 元素,代码如下所示。
<?xml version="1.0" encoding="UTF-8"?>
MyServlet
net.biancheng.www.MyServlet
1
MyServlet
/myServlet
MyServlet
/myServlet2
启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/myServlet”,访问 MyServlet,结果如下图。

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

##### 2. 配置多个 子元素
从 Servlet 2.5 开始, 元素可以包含多个 子元素,每个 代表一个虚拟路径的映射规则。因此,通过在一个 元素中配置多个 子元素,也可以实现 Servlet 的多重映射。
以 servletDemo 为例, 元素下添加两个 子元素,代码如下。
<?xml version="1.0" encoding="UTF-8"?>
MyServlet
net.biancheng.www.MyServlet
1
MyServlet
/myServlet
/myServlet3
启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/myServlet”,访问 MyServlet,结果如下图。

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

##### 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,结果如下图。

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

## 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 的配置如下。
<?xml version="1.0" encoding="UTF-8"?>
MyServlet1
net.biancheng.www.VirtualPathServlet
MyServlet2
net.biancheng.www.VirtualPathServlet
MyServlet3
net.biancheng.www.VirtualPathServlet
MyServlet4
net.biancheng.www.VirtualPathServlet
MyServlet5
net.biancheng.www.VirtualPathServlet
MyServlet1
/hello
MyServlet2
/abc/my/
MyServlet3
/abc/
MyServlet4
*.do
MyServlet5
/
启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/hello”,结果如下图。

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

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

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

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

### Tomcat 中的缺省 Servlet
在 Tomcat 安装目录的 \conf\web.xml 文件中,注册了一个名称为 org.apache.catalina.servlets.DefaultServlet 的 Servlet,并将它设置为缺省 Servlet。
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Tomcat 服务器中的 Web 应用没有缺省 Servlet 时,会将 DefaultServlet 作为其缺省 Servlet。当客户端访问 Tomcat 服务器中某个静态 HTML 文件或者图片时,DefaultServlet 会判断该 HTML 或图片是否存在,若存在,则将数据以流的形式返回客户端,否则会报告 404 错误。
##### 示例 2
将 servletDemo 应用的缺省匹配删除,并在 servletDemo 应用里添加一张图片 a.jpg。
<?xml version="1.0" encoding="UTF-8"?>
启动 Tomcat,在地址栏输入“http://localhost:8080/servletDemo/a.jpg”,结果如下图。

## 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 配置初始化参数,代码如下。
<?xml version="1.0" encoding="UTF-8"?>
MyServlet
net.biancheng.www.MyServlet
name
编程帮
URL
www.biancheng.net
以上配置说明如下:
* 元素是 的子元素, 需要在 元素内使用,表示只对当前 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 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,结果如下图。

## 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 文件中配置上下文初始化参数,代码如下所示。
<?xml version="1.0" encoding="UTF-8"?>
name
编程帮
url
www.biancheng.net
对以上标签说明如下:
* 元素用来声明上下文初始化参数,必须在根元素 内使用。
* 子元素表示参数名,参数名在整个 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 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,结果如下图。

#### 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 次,结果如下图。

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

#### 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,结果如下图。

## HttpServletRequest接口详解
一般情况下,浏览器(客户端)通过 HTTP 协议来访问服务器的资源,Servlet 主要用来处理 HTTP 请求。
Servlet 处理 HTTP 请求的流程如下:

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,结果如下图。

##### 获取请求头信息
当浏览器发送请求时,需要通过请求头向服务器传递一些附加信息,例如客户端可以接收的数据类型、压缩方式、语言等。为了获取请求头中的信息, 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 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 ,结果如下图。

##### 获取 form 表单的数据
在实际开发中,我们经常需要获取用户提交的表单数据,例如用户名和密码等。为了方便获取表单中的请求参数,ServletRequest 定义了一系列获取请求参数的方法,如下表所示。
| 返回值类型 | 方法声明 | 功能描述 |
| --- | --- | --- |
| String | getParameter(String name) | 返回指定参数名的参数值。 |
| String [ ] | getParameterValues (String name) | 以字符串数组的形式返回指定参数名的所有参数值(HTTP 请求中可以有多个相同参数名的参数)。 |
| Enumeration | getParameterNames() | 以枚举集合的形式返回请求中所有参数名。 |
| Map | getParameterMap() | 用于将请求中的所有参数名和参数值装入一个 Map 对象中返回。 |
##### 示例 3
为了更好地理解这些方法,下面通过案例演示这些方法的使用。
在 httpServletRequestDemo 的 WebContent 目录下,创建 form.html,代码如下。
编程帮wwww.biancheng.net | |
输入姓名 | |
输入密码 | |
选择性别 | 男 女 |
选择使用的语言 | JAVA C语言 PHP Python |
选择城市 | --请选择-- 北京 上海 广州 |
在 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,结果如下图。

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

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

根据请求方式的不同,请求一般可以被分为两种: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 资源进行处理。请求转发的工作原理如下图所示。

##### 请求转发的特点
请求转发具有以下特点:
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,代码如下。
编程帮wwww.biancheng.net | |
输入姓名 | |
输入密码 | |
选择性别 | 男 女 |
选择使用的语言 | JAVA C语言 PHP Python |
选择城市 | --请选择-- 北京 上海 广州 |
启动 Tomcat 服务器,在地址栏输入“http://localhost:8080/httpServletRequestDemo/login.html”,访问 login.html,结果如下图。

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

## 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,结果如下图。

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

### 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”);
惊喜
最后还准备了一套上面资料对应的面试题(有答案哦)和面试时的高频面试算法题(如果面试准备时间不够,那么集中把这些算法题做完即可,命中率高达85%+)
ttpServletResponse 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,结果如下图。

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

### 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”);
惊喜
最后还准备了一套上面资料对应的面试题(有答案哦)和面试时的高频面试算法题(如果面试准备时间不够,那么集中把这些算法题做完即可,命中率高达85%+)
[外链图片转存中…(img-3OHB936e-1714508664840)]
[外链图片转存中…(img-flvOzhUA-1714508664841)]