Servlet(二)

🍵 Servlet简称Server Applet(即服务器端小程序),是一种基于Java技术的Web组件,运行在服务器端。

1 生命周期

  • 生命周期
    • Servlet的生命周期就是Servlet从创建到销毁的过程,由Servlet容器管理。
    • Servlet生命周期主要分为初始化阶段、运行时阶段、销毁阶段这三个阶段。
    • 在javax.servlet.Servlet接口中定义了3个方法:init()、service()、destory()。
    • 启动服务,多次访问地址:http://localhost:8080/servletDemo/MyServlet21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet21")
public class MyServlet21 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 + "<br/>" + "请求次数:" +
httpCount + "<br/>" + "销毁次数:" + destoryCount
);
writer.close();
}

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

2 ServletConfig

  • ServletConfig
    • 容器初始化Servlet时,会为Servlet创建一个ServletConfig对象并将其作为参数传递给Servlet。
    • 一个Web应用中可以存在多个ServletConfig对象,一个Servlet只能对应一个ServletConfig对象。
    • 也就是说Servlet的初始化参数仅对当前Servlet有效。

2-1 获取对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}

// 调用GenericServlet提供的getServletConfig()方法获得ServletConfig对象
ServletConfig servletConfig = this.getServletConfig();

2-2 初始化参数

  • 初始化参数
    • 配置Servlet的初始化参数方式:web.xml、@WebServlet。
    • web.xml中可使用一个或多个<init-param>元素进行配置。
    • 通过@WebServlet的initParams属性也可以为Servlet设置初始化参数。
    • 启动服务,访问:http://localhost:8080/servletDemo/MyServlet22

(1) web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<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">
<display-name>servletDemo</display-name>

<servlet>
<servlet-name>MyServlet22</servlet-name>
<servlet-class>MyServlet22</servlet-class>

<!-- Servlet初始化参数 -->
<init-param>
<param-name>名称</param-name>
<param-value>web.xml配置Servlet的初始化参数</param-value>
</init-param>

<!-- Servlet初始化参数 -->
<init-param>
<param-name>链接</param-name>
<param-value>www.baidu.com</param-value>
</init-param>
</servlet>
</web-app>

(2) @WebServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(
urlPatterns = { "/MyServlet22" }, initParams = {
@WebInitParam(name = "名称", value = "@WebServlet属性配置Servlet的初始化参数"),
@WebInitParam(name = "链接", value = "www.baidu.com")
}
)
public class MyServlet22 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();
// 获取ServletConfig对象
ServletConfig config = getServletConfig();
// 返回servlet的初始化参数的名称的集合
Enumeration<String> initParameterNames = config.getInitParameterNames();
// 遍历集合获取初始化参数名称
while (initParameterNames.hasMoreElements()) {
// 获取初始化参数名称
String initParamName = initParameterNames.nextElement();
// 获取相应的初始参数的值
String initParamValue = config.getInitParameter(initParamName);
// 向页面输出
writer.write(initParamName + ":" + initParamValue + "<br/>");
}
// 关闭流
writer.close();
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doGet(req, resp);
}
}

3 ServletContext

  • ServletContext
    • 容器启动时,会为每个Web应用创建唯一的ServletContext对象,称其为Servlet上下文。
    • ServletContext对象的生命周期从Servlet容器启动时开始,到关闭或应用被卸载时结束。
    • Web应用(即webapps下的每个目录都是)中的所有Servlet共享同一个ServletContext对象。
    • 不同Servlet之间可以通过ServletContext对象(也被称为Context域对象)来实现数据通讯。

3-1 获取对象

1
2
3
4
5
6
7
8
9
10
11
// 通过GenericServlet提供的getServletContext()方法获取ServletContext对象
ServletContext servletContext = this.getServletContext();

// 通过ServletConfig提供的getServletContext()方法获取ServletContext对象
ServletContext servletContext = this.getServletConfig().getServletContext();

// 通过HttpSession提供的getServletContext()方法获取ServletContext对象
ServletContext servletContext = req.getSession().getServletContext();

// 通过HttpServletRequest提供的getServletContext()方法获取ServletContext对象
ServletContext servletContext = req.getServletContext();

3-2 初始化参数

  • 初始化参数
    • 通过web.xml文件中的<context-param>元素可以为Web应用设置一些全局的初始化参数。
    • 这些参数被称为上下文初始化参数,应用中的所有Servlet都共享同一个上下文初始化参数。
    • Web应用的整个生命周期里,上下文初始化参数将一直在且随时可被任意一个Servlet访问。
    • Servlet容器启动时,会为容器内每个Web应用创建一个ServletContext对象。
    • 并将<context-param>元素中的上下文初始化参数以键值对形式存入对象中。
    • 启动服务器,访问地址:http://localhost:8080/servletDemo/MyServlet23

(1) web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<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">
<display-name>servletDemo</display-name>

<!-- 设置全局初始化参数 -->
<context-param>
<param-name>名称</param-name>
<param-value>web.xml设置全局初始化参数</param-value>
</context-param>
<context-param>
<param-name>链接</param-name>
<param-value>www.baidu.com</param-value>
</context-param>
</web-app>

(2) Servlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet23")
public class MyServlet23 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 + "<br/>");
}
// 关闭流
writer.close();
}

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

3-3 实现数据通讯

  • 实现数据通讯
    • 在Servlet中调用ServletContext接口的setAttribute()方法可以创建一些属性。
    • 应用中所有Servlet都可以对这些属性进行访问和操作,实现应用内的数据通讯。
    • 编写1个统计页面访问量的案例,来演示通过ServletContext对象实现数据通讯。
    • 先创建一个MyServlet24的Servlet类,然后创建一个MyServlet25的Servlet类。
    • 重启服务,多次访问地址:http://localhost:8080/servletDemo/MyServlet24
    • 最后,再次访问地址链接:http://localhost:8080/servletDemo/MyServlet25

(1) 自增类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet24")
public class MyServlet24 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("<h3>欢迎访问www.baidu.com!</h3>");
}

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

(2) 访问类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet25")
public class MyServlet25 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");
// 若MyServlet24已被访问
if (count != null) {
response.getWriter().write("<h3>该网站一共被访问了" + count + "次!</h3>");
} else {
// 若MyServlet24未被访问,提示先访问MyServlet24
response.getWriter().write("<h3>请先访问MyServlet24!</h3>");
}
}

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

3-4 读取资源文件

  • 读取资源文件
    • 实际开发中,可能需要读取Web应用中的一些资源文件,如配置文件或日志等。
    • 在/servletDemo/src/webapp/WEB-INF/classes目录中创建db.properties文件。
    • 创建MyServlet26类,访问:http://localhost:8080/servletDemo/MyServlet26

(1) db.properties

1
2
3
name=\u767E\u5EA6\u4E00\u4E0B
url=www.baidu.com
desc=\u6B22\u8FCE\u8BBF\u95EE\u767E\u5EA6\u7F51\u7AD9\uFF01

(2) 创建一个新类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet26")
public class MyServlet26 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 + "<br/>" +
"链接:" + url + "<br/>" + "描述:" + desc + "<br/>"
);
}

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

4 HttpServletRequest

  • HttpServletRequest
    • 浏览器(客户端)通过HTTP协议访问服务器资源,Servlet主要用来处理HTTP请求。
    • HttpServletRequest和HttpServletReponse是Servlet处理HTTP请求流程中的两个重要对象。
    • HttpServletRequest用于封装HTTP请求信息,HttpServletReponse用于封装HTTP响应信息。
      • Servlet API中定义了一个HttpServletRequest接口,继承自ServletRequest。
      • HttpServletRequest对象专门用于封装HTTP的请求消息,简称为request对象。
      • HttpServletRequest接口定义了获取请求行、请求头和请求消息体的相关方法。

4-1 获取请求行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet27")
public class MyServlet27 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() +
"<br/>" + "请求字符串:" + request.getQueryString() +
"<br/>" + "客户端IP地址:" + request.getRemoteAddr() +
"<br/>" + "应用名称(上下文):" + request.getContextPath() +
"<br/>" + "URI:" + request.getRequestURI() +
"<br/>" + "Servlet所映射的路径:" + request.getServletPath() +
"<br/>" + "客户端的完整主机名:" + request.getRemoteHost() + "<br/>"
);
}

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

4-2 获取请求头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet28")
public class MyServlet28 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 + "<br/>");
}
}

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

4-3 获取form表单

(1) form.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>

<body>
<form action="/servletDemo/MyServlet29" method="post">
<table border="1" width="50%">
<tr>
<td colspan="2" align="center">HttpServletRequest获取form表单</td>
</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" />Python
</td>
</tr>
<tr>
<td>选择城市</td>
<td>
<select name="city">
<option value="none">--请选择--</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
<option value="shenzhen">深圳</option>
</select>
</td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="提交" /></td>
</tr>
</table>
</form>
</body>

</html>

(2) 创建新类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.util.Arrays;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet29")
public class MyServlet29 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 +
"<br/>" + "密码:" + password +
"<br/>" + "性别:" + sex +
"<br/>" + "城市:" + city +
"<br/>" + "语言:" + Arrays.toString(languages) + "<br/>"
);
}

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

4-4 中文乱码问题

  • 中文乱码问题
    • 获取form表单中,输入中文姓名时可能出现乱码问题。
    • GET和POST这两种请求方式都可能产生中文乱码情况。
    • POST
      • 提交的数据在请求体中所使用的编码格式是UTF-8。
      • request对象将数据放到缓冲区时使用ISO-8859-1。
    • GET
      • 浏览器发送文字时采用的编码格式与页面编码保持一致(UTF-8格式)。
      • 若Tomcat未设置字符集,接收URL时默认使用ISO-8859-1导致乱码。
      • Tomcat版本8及以上,已成功解决了该方式提交请求中文乱码的问题。

(1) POST

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

(2) GET修改配置

1
2
3
4
5
6
7
<!-- tomcat/conf/server.xml配置 -->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxParameterCount="1000"
URIEncoding="UTF-8"
/>

(3) GET逆向编解码

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

(4) String构造方法

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

5 HttpServletResponse

  • HttpServletResponse
    • 在Servlet API中定义了HttpServletResponse接口,继承自ServletResponse。
    • HttpServletResponse对象专门用来封装HTTP响应消息,简称为response对象。
    • HttpServletResponse定义了向客户端发送响应状态码、响应头、响应体的方法。

5-1 创建Servlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet30")
public class MyServlet30 extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 调用run1(),访问正常
// run1(response);
// 调用run2(),使用字符流输出响应内容到浏览器,访问中文乱码
run2(response);
}

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

// 使用字节流向页面输出
public void run1(HttpServletResponse response) throws IOException {
// 设置浏览器打开文件时编码
response.setHeader("Content-Type", "text/html;charset=UTF-8");
// 获取字节输出流
OutputStream os = response.getOutputStream();
byte[] str = "欢迎访问首页【MyServlet30】!".getBytes("UTF-8");
// 输出中文
os.write(str);
}

// 使用字符流向页面输出
public void run2(HttpServletResponse response) throws IOException {
response.getWriter().write("欢迎访问首页【MyServlet30】!");
}
}

5-2 中文乱码问题

  • 中文乱码问题
    • response对象向页面输出时有两种方式:字节流、字符流,输出中文时都可能出现乱码。
    • 字节流输出
      • 是否乱码取决于中文转成字节数组时与浏览器打开时采用的字符集是否一致。
      • 如果两者保持一致,则不会出现乱码问题,如果不一致,就会出现乱码问题。
    • 字符流输出
      • 输出中文一定会乱码,通过字符流输出的内容存放在response缓冲区。
      • response缓冲区的默认字符集是ISO-8859-1,该字符集不支持中文。

(1) 字节流输出

1
2
3
4
5
6
7
8
9
10
11
// 使用字节流输出
ServletOutputStream outptuStream = response.getOutputStream();
outputStream.write(“欢迎访问首页【MyServlet30】!”.getBytes());

// 解决方法:将中文转成字节数组时和浏览器默认采用的字符集保持一致
response.setHeader("Content-Type", "text/html;charset=UTF-8");
// 获取字节输出流
OutputStream os = response.getOutputStream();
byte[] str = "欢迎访问首页【MyServlet30】!".getBytes("UTF-8");
// 输出中文
os.write(str);

(2) 字符流输出

1
2
3
4
5
6
7
8
9
10
11
12
// 解决方法:将response缓冲区和浏览器采用的字符集保持一致

// 方法一:设置response缓冲区的编码
response.setCharacterEncoding("UTF-8");
// 设置浏览器打开文件所采用的编码
response.setHeader("Content-Type", "text/html;charset=UTF-8");
// 输出中文
response.getWriter().write("欢迎访问首页【MyServlet30】!");

// 方法二
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("欢迎访问首页【MyServlet30】!");

6 RequestDispatcher接口

  • RequestDispatcher接口
    • Web应用在处理客户端的请求时,经常需要多个Web资源共同协作才能生成响应结果。
    • 但Servlet对象无法直接调用其他service()方法,因此提供请求转发、请求包含两方案。
      • 请求转发属于服务器行为,容器接收请求后,Servlet会先对请求做一些预处理。
      • 然后再将请求传递给其他的Web资源,来完成包括生成响应在内的其他后续工作。
    • javax.servlet包定义了RequestDispatcher接口,用于封装由路径所标识的Web资源。
    • RequestDispatcher对象由Servlet容器创建,获取RequestDispatcher对象的方式如下。
      • ServletContext的getRequestDispatcher(String path):path指定目标资源的路径,必须是绝对路径。
      • ServletRequest的getRequestDispatcher(String path):path指定目标资源的路径,绝对或相对路径。
      • 绝对路径以符号“/”开头的,表示当前Web应用的根目录,相对路径则是指相对于当前Web资源的路径。
    • RequestDispatcher接口提供的方法
      • include(ServletRequest request, ServletResponse response):将其他资源作为响应包含进来。
      • forward(ServletRequest request, ServletResponse response):将请求转发给另一个Web资源。

6-1 request域对象

  • request域对象
    • request是Servlet的三大域对象之一,需与请求转发配合使用,才可实现动态资源间的数据传递。
    • request域对象 vs Context域对象
      • request域对象:与请求转发配合使用实现数据共享。
      • Context域对象:独立完成动态资源之间的数据共享。
      • request域对象:从客户端向容器发送请求开始,到对这次请求做出响应后结束。
      • Context域对象:生命周期从容器启动开始,到关闭或者Web应用被移除时结束。
      • request域对象:只对本次请求涉及的Servlet有效,每个Servlet实例都可以有多个request。
      • Context域对象:对于整个Web应用的所有Servlet都有效,整个Web应用只有一个Context。

6-2 创建一请求类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet31")
public class MyServlet31 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("<h1>这是转发前在响应信息中的内容!</h1>");
// 向reuqest域对象中添加属性,传递给下一个web资源
request.setAttribute("webName", "Servlet的请求转发");
request.setAttribute("url", "https://www.baidu.com/");
request.setAttribute("welcome", "百度一下,你就知道!");
// 转发
request.getRequestDispatcher("/MyServlet32").forward(request, response);
}

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

6-3 创建一转发类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import java.util.Arrays;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet32")
public class MyServlet32 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("<h3>" + webName + "</h3>");
}
if (url != null) {
writer.write("<h3>" + url + "</h3>");
}
if (welcome != null) {
writer.write("<h3>" + welcome + "</h3>");
}
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 + "<br/>" + "密码:" + password + "<br/>" +
"性别:" + sex + "<br/>" + "城市:" + city + "<br/>" +
"语言:" + Arrays.toString(languages) + "<br/>"
);
}

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

6-4 创建login.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title>Servlet的请求转发</title>
</head>

<body>
<form action="/servletDemo/MyServlet31" method="GET">
<table border="1" width="50%">
<tr>
<td colspan="2" align="center">Servlet的请求转发</td>
</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" />Python
</td>
</tr>
<tr>
<td>选择城市</td>
<td><select name="city">
<option value="none">--请选择--</option>
<option value="北京">北京</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>

7 重定向sendRedirect()

  • 重定向sendRedirect()
    • 重定向属客户端行为,服务器收到客户端请求后通知浏览器重新向另1个URL发送请求。
    • 请求重定向的本质实际是两次HTTP请求,对应了两个request对象和两个response对象。

7-1 创建验证类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import java.awt.Font;
import java.util.List;
import java.awt.Color;
import java.util.Random;
import java.awt.Graphics2D;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet33")
public class MyServlet33 extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int width = 120;
int height = 30;
// 在内存中生成图片
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 先获取画笔对象
Graphics2D g = (Graphics2D) image.getGraphics();
// 设置灰色
g.setColor(Color.GRAY);
// 画填充的矩形
g.fillRect(0, 0, width, height);
// 设置颜色
g.setColor(Color.BLUE);
// 画边框
g.drawRect(0, 0, width - 1, height - 1);
// 准备数据,随机获取4个字符
String words = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
// 设置颜色
g.setColor(Color.YELLOW);
// 设置字体
g.setFont(new Font("隶书", Font.BOLD, 20));
String code = "";
// 构造存储字符串的集合
List<String> list = new ArrayList<String>();
Random random = new Random();
int x = 20;
int y = 20;
for (int i = 0; i < 4; i++) {
// 获取正负30之间的角度
int ang = random.nextInt(60) - 30;
double rad = ang * Math.PI / 180;
g.rotate(rad, x, y);
// 获取下标
int index = random.nextInt(words.length());
// 返回指定下标位置的字符,随机获取下标
char ch = words.charAt(index);
// 将字符存入字符数组中
char[] chc = { ch };
// 使用字符数组构造字符串
String string = new String(chc);
// 将构造好的字符串添加进list集合中
list.add(string);
// 写字符串
g.drawString("" + ch, x, y);
g.rotate(-rad, x, y);
x += 20;
}
for (int i = 0; i < list.size(); i++) {
code += list.get(i);
}
// 将验证码存入上下文中
getServletContext().setAttribute("code", code);
// 设置颜色
g.setColor(Color.GREEN);
int x1, x2, y1, y2;
// 画干扰线
for (int i = 0; i < 4; i++) {
x1 = random.nextInt(width);
y1 = random.nextInt(height);
x2 = random.nextInt(width);
y2 = random.nextInt(height);
g.drawLine(x1, y1, x2, y2);
}
// 输出到客户端
ImageIO.write(image, "jpg", response.getOutputStream());
}

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

7-2 创建提交类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet34")
public class MyServlet34 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");
String username = request.getParameter("username");
// 获取密码
String password = request.getParameter("password");
// 获取验证码
String code = request.getParameter("code");
// 设置是否成功标识
Boolean IsSuccess = true;
// 从上下文获取存储的验证码
String code1 = (String) getServletContext().getAttribute("code");
// 账号密码为admin,并且验证码(忽略大小写)输入正确,则跳转到登录成功页面
if (!"".equals(code) && code != null && code.equalsIgnoreCase(code1)
&& "admin".equals(username) && "admin".equals(password)) {
response.sendRedirect("/servletDemo/MyServlet36");
// 账号密码不为admin,设置错误信息
} else if (!"admin".equals(username) || !"admin".equals(password)) {
getServletContext().setAttribute("msg", "账号或密码不正确");
IsSuccess = false;
// 验证码错误,设置错误信息
} else if ("".equals(code) || code == null || !code.equalsIgnoreCase(code1)) {
getServletContext().setAttribute("msg", "验证码输入错误");
IsSuccess = false;
}
if (!IsSuccess) {
// 设置自动跳转的时间,存储在上下文中
getServletContext().setAttribute("time", 5);
// 向request对象中设置属性requestAttr,在重定向之后取值
request.setAttribute("requestAttr", "重定向中使用request域对象传递的数据");
response.sendRedirect("/servletDemo/MyServlet35");
}
}

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

7-3 创建定向类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import java.util.Date;
import java.util.Calendar;
import java.io.IOException;
import java.text.SimpleDateFormat;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyServlet35")
public class MyServlet35 extends HttpServlet {
private static final long serialVersionUID = 1L;

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Object requestAttr = request.getAttribute("requestAttr");
// 获取存在上下文中的跳转时间
int times = (int) getServletContext().getAttribute("time");
// 获取存在上下文中的错误信息
String msg = (String) getServletContext().getAttribute("msg");

response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", -1);

response.setContentType("text/html;charset=UTF-8");
// 设置提示
String title = msg + ",即将在" + times + "秒钟后跳转到登录页";
// 使用默认时区和语言环境获得一个日历
Calendar cale = Calendar.getInstance();
// 将Calendar类型转换成Date类型
Date tasktime = cale.getTime();
// 设置日期输出的格式
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 格式化输出
String nowTime = df.format(tasktime);
// 只要倒计时时间没有到0,就直至输出下面内容
if (times != 0) {
response.getWriter().write(
"<html>\n" + "<head><title>Servlet的重定向</title></head>\n"
+ "<body bgcolor=\"#f0f0f0\">\n"
+ "<h1 align=\"center\">百度一下 https://www.baidu.com/ 提醒您:</h1>"
+ "<h1 align=\"center\" style=\"font-family:arial;color:red;\">" + title + "</h1>\n"
+ "<h1 align=\"center\">当前时间为:" + nowTime + "</h1>\n"
+ "<h1 align=\"center\">重定向通过request传递的数据为:" + requestAttr + "</h1>\n"
);
// 倒计时
times--;
// 将倒计时的时间重新存入上下文中覆盖原来的值
getServletContext().setAttribute("time", times);
// 通过refresh头完成页面刷新
response.setHeader("refresh", "1;url=/servletDemo/MyServlet35");
} else {
// 倒计时归零,则跳转到登陆页面
response.sendRedirect("/servletDemo/WebContent/register.html");
}
}

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

7-4 创建登录类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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("/MyServlet36")
public class MyServlet36 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.write("<head><title>Servlet的重定向</title></head>");
writer.write("<center><h1>恭喜~登录成功!</h1></center>");
}

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

7-5 register.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<!DOCTYPE html>
<html>

<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Servlet的重定向</title>
</head>

<body>
<form action="/servletDemo/MyServlet34" method="GET">
<table border="1" width="50%">
<tr>
<td colspan="2" align="center">Servlet的重定向</td>
</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" />Python
</td>
</tr>
<tr>
<td>选择城市</td>
<td>
<select name="city">
<option value="none">--请选择--</option>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
<option value="深圳">深圳</option>
</select>
</td>
</tr>
<tr>
<td>验证码</td>
<td><input type="text" name="code" />
<img id="imgId" src="/servletDemo/MyServlet33">
<a href="#" onclick="run()">看不清,换一张!</a>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="提交" />
</td>
</tr>
</table>
</form>
</body>
<script type="text/javascript">
// 看不清,换一张,时间戳
function run() {
// 获取图片
var image = document.getElementById("imgId");
image.src = "/servletDemo/MyServlet33?" + new Date().getTime();
}
</script>

</html>

Servlet(二)
https://stitch-top.github.io/2025/10/25/java/java06-servlet-er/
作者
Dr.626
发布于
2025年10月25日 15:50:00
许可协议