1. 什么是 Servlet?
先让时间回到 25 年前,我国刚刚接入互联网不到两年时间。那时候的电脑长这个样子:
当时的网页技术还不是很发达,大家打开浏览器只能浏览一些静态 的页面,例如图片、文本信息等。?
随着时间的发展,静态页面已经不能满足于大部分用户的需求了。
用户打开浏览器不能只浏览个图片和文字吧?用户还想提交个表单、点个按钮、还想和浏览器做交互怎么办?
为了解决用户和浏览器动态交互的问题,到了1997年,SUN 公司(发明了JDK) 推出了Servlet 技术。
Servlet 是一个基于 Java 技术开发的 web 组件,它运行在服务器端,由?Servlet容器 管理。主要用于处理用户的请求并生成动态 的信息。
Servlet 其实就是按照?Servlet规范 编写的一个 Java 类。
我们知道一个类要想独立运行必须要有 main 方法,但是 Servlet 这个类没有 main 方法。那怎么运行?
答案是放到 servlet 容器里运行。
你可以把 servlet 看成是一个个的 app ,而手机就相当于一个 servlet 容器。app 不能单独运行,必须放在手机上运行。?
?servlet 因为没有 main 方法,也不能独立运行,必须放在 servlet 容器里面运行。?
2. Tomcat 的安装和使用
2.1 Tomcat 简介
前面我们讲到 servlet 必须放到servlet容器 里面运行,而?Tomcat ?就是一个可以运行 servlet 的容器。
为什么推荐使用 Tomcat?因为它免费、开源、好用。
Tomcat 响应用户请求的过程如下:
-
- 用户通过浏览器向 Tomcat(web服务器)发送一个 HTTP 请求。
-
- Tomcat 接收到请求后,将请求信息发送给 Servlet 容器,并交给servlet 容器一个
请求对象 和一个响应对象 。
-
- Servlet 容器加载 Servlet,先创建一个?
Servlet实例 。然后告诉这个 servlet 实例说:嘿!小伙,我这里有一个用户的请求对象 和响应对象 ,你来处理一下。
-
- Servlet 实例从
请求对象 拿到客户端的请求信息,然后进行相应的处理。
-
- Servlet 实例将处理结果交给
响应对象 ,通过响应对象发送到客户端。
注:
- 请求对象:HttpServletRequest
- 响应对象:HttpServletResponse
2.2 Tomcat 的安装与配置
注:安装 Tomcat 之前需要先安装配置 JDK。
链接:https://pan.baidu.com/s/1Ey-gg8tpHT9P-xNOUcrZmg
提取码:1234
复制代码
进入 bin 目录下,双击 startup.sh?
?打开浏览器,输入:
http://localhost:8080/
复制代码
当出现如下画面,说明你安装成功了!!!?
3. Servlet 规范
-
- servlet 规范定义了动态资源文件(其实就是servlet)的开发步骤。
-
- servlet 规范定义了 Tomcat 服务器
调用 ?servlet 的规则。
-
- servlet 规范定义了 Tomcat 服务器
管理 ?servlet 实例的规则。
为什么要定义这些规范?
因为你要写一个 servlet 类交给 Tomcat 管理、运行,你得听人家的。俗话说人在屋檐下,哪有不低头?所以你必须按照人家 Tomcat 指定的规范去开发。
4. 开发 Servlet
4.1 新建一个 javaweb 项目
这里我们使用 idea 创建一个 javaweb 项目。?
?
?项目目录:?
4.2 idea 配置 Tomcat
1.顶部菜单栏选择 Run,选择 Edit Configuration。
2.点击“+”号,选择 Tomcat Server,选择 Local。
?3.Application server 选择 Tomcat 本地安装目录。Http port 默认 8080 即可。?
?4.选择 Deployment,点击右边的+ 号,选择 Artifact。?
?5.选择 web 项目,Application context 可以自定义,它相当于我们项目的默认访问路径。?
?6.编写 index.jsp?
?7.运行 Tomcat?
?浏览器网址输入:
http://localhost:8080/myProject/
复制代码
4.3 开发 Servlet
1.引入 jar 包
在 WEB-INF 目录下新建 lib 目录,将 Tomcat bin 目录下的?servlert-api.jar 放到 lib 项目下。?
?
2.将 jar 包作为库添加到项目中。
注:因为 jar 包就是编译后的 java 代码,所以这里就相当于在项目中添加了 Tomcat 指定规范下的 java 代码。
选择 lib 目录,右键选择 Add as a Library。?
?3.新建 Servlet 类
Servlet规范:新建的 Servlet 类要想被 Tomcat 管理,必须继承 Tomcat jar 包下的 HttpServlet 类。
创建一个 Java 类继承 HttpServlet 父类,使之成为一个 Servlet 接口实现类,并重写父类doGet 和doPost 方法。
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这里用来处理用户的 get 请求
System.out.println("接收到用户的 get 请求");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这里用来处理用户的 post 请求
System.out.println("接收到用户的 post 请求");
}
}
复制代码
4.配置 web.xml
为什么要配置 web.xml ?
你根据 servlet 规范创建了一个 servlet 类,但是大哥 Tomcat 不知道啊!所以你需要告诉大哥一声。
web.xml 相当于 Tomcat 的门卫,你需要在门卫处做一下登记,将Servlet接口实现类信息注册 到Tomcat服务器。
你需要登记 servlet 类的名字、类所在项目的路径以及请求路径。?
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--将 Servlet接口实现类交给Tomcat管理-->
<servlet>
<servlet-name>userServlet</servlet-name> <!--Servlet接口实现类名称-->
<servlet-class>com.xxl.controller.UserServlet</servlet-class><!--声明servlet接口实现类类路径-->
</servlet>
<servlet-mapping>
<servlet-name>userServlet</servlet-name>
<url-pattern>/user</url-pattern> <!--设置Servlet类的请求路径,必须以 / 开头-->
</servlet-mapping>
</web-app>
复制代码
- servlet-name:servlet 类的名字。
- servlet-class:servlet 类所在项目的路径。
- url-pattern:用户请求该 servlet 的路径。
5.测试
运行项目,浏览器输入:
http://localhost:8080/myProject/user
复制代码
查看控制台打印:?
注: 浏览器直接输入网址,按回车键是 GET 请求,和 html 中的超链接一样,例如:
<a href="/myProject/user">点击访问 UserServlet</a>
复制代码
6.注解式开发
时间从敲代码的键盘声中划过,貌似一切风平浪静,可是有一天却出事了。
那是一个黑云压城城欲摧 的下午,Tomcat 的门卫web.xml 正在门卫室一边嗑瓜子一遍看报纸,门外突然传来了一阵阵急促的马蹄声。
原来这一天下午突然来了一万个 servlet 找 web.xml 做登记。
web.xml 一看这阵势蚌埠住了! ,赶紧禀告 Tomcat 大官人。
Tomcat 一想:"这一万个 servlet 得登记到猴年马月啊?算了,不让他们做登记了,让他们自己身上带个标记 吧,这个标记我认得就行。"
所以在servlet3.0 版本之后,我们不需要在 web.xml 里面给每个 servlet 做配置了,只需要加一个注解就行。注解格式:
@WebServlet(name = "servlet的名字",value = "访问路径")
或者:
@WebServlet("/访问路径") //默认以类名作为名字
复制代码
例如:
@WebServlet(name = "userServlet",value = "/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这里用来处理用户的 get 请求
System.out.println("哈哈哈哈哈哈我头上有注解");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这里用来处理用户的 post 请求
System.out.println("接收到用户的 post 请求");
}
}
复制代码
或者:
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这里用来处理用户的 get 请求
System.out.println("哈哈哈哈哈哈我头上有注解");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这里用来处理用户的 post 请求
System.out.println("接收到用户的 post 请求");
}
}
复制代码
这时的 web.xml 就是空空如也:?
?即便 web.xml 什么也不配置,因为我们加了注解 ,所以 Tomcat 也能识别到,我们再来请求一下 servlet,发现也能请求成功。
运行项目,浏览器输入:
http://localhost:8080/myProject/user
复制代码
?注 :名字和请求路径要保持唯一性,不然 Tomcat 获取用户的请求之后不知道给哪一个 Servlet。
5. Http 协议
5.1 简介
为什么要学习 Http 协议?
因为只有知道了 Http 协议,你才能弄清楚信息在网络上传递的过程,你才能知道 servlet 是干什么的。
Htttp 协议主要应用在客户端-服务端架构上。浏览器作为 Http 客户端通过 URL 向 Web 服务器发送请求,其中 Http 常用的请求的方式主要有 GET 请求和 POST 请求。Web 服务器处理用户的请求后,向客户端发送响应信息。
信息在网络上都是以二进制 形式进行传递的,传递的信息被封装成一个个的 Http 协议包。Http 协议包包括请求包和响应包。
5.2 Http 请求
HTTP 请求的消息格式主要包含:请求行、请求头、请求空行、请求数据。
例如 GET 请求的消息体:
GET /login.html HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
复制代码
请求行 主要包括:请求方式、请求的URL、请求的 Http 版本协议,例如:
GET /login.html HTTP/1.1
复制代码
请求头格式:请求头名称: 请求头值,例如:
Host: localhost
复制代码
下面是一个完整的请求头:
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
复制代码
请求空行就是一个空行,意思就是告诉服务器从下一行开始就没有请求头了。
GET 请求是没有请求体的,只有 POST 请求有请求体,请求体是以键值对的方式传输的,POST 的请求体就是键值对。?
5.3 Http 响应
HTTP 响应的消息格式主要包含:状态行、响应头、空行、相应数据。 例如下面就是一个完整的响应体:
HTTP/1.1 200 OK
Date: Wed, 22 Dec 2021 09:07:21 GMT
Content-Type: text/html; charset=UTF-8
<html>
<head></head>
<body>
一颗雷布斯
</body>
</html>
复制代码
状态行:HTTP协议版本号、状态码、状态消息。
第一行为状态行,(HTTP/1.1) 表示 HTTP 版本为 1.1 版本,状态码为 200,状态消息是 ok。
第二行和第三行为响应头,用来说明客户端要使用的一些附加信息。
第四行是空行。
最后的 html 代码就是响应的正文。
5.4 GET 和 POST 区别
- GET 把请求参数包含在 URL 中,POST 通过请求体传递参数。
- GET 不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息。
- GET 请求在 URL 中传递的参数是有长度限制的,而 POST 没有限制。
6. 欢迎页面
我们平时访问一个网站,都是输入一个简单的网址,例如:
https://www.baidu.com
复制代码
而不是:
https://image.baidu.com/search/index?tn=baiduimage&ct=201326592
复制代码
因为这种太长的路径我们根本记不住。所以当用户访问一个网站时,我们一般会设置一个默认的页面,例如百度默认首页:?
?那如何在我们的项目中设置默认的欢迎页面?
也是在 web.xml 中配置。设置规则:
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
复制代码
然后我们需要在web 或者webapp 目录下创建欢迎页面。
欢迎页面默认显示顺序是从上到下,如果不配置也是从这几个文件中找。
比如我们刚创建这个项目时没有配置欢迎页面,但是程序运行之后默认显示的就是 index.jsp 的内容。
当然了我们也可以设置自定义的欢迎页面,例如:?
?login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login.html</title>
</head>
<body>
<h1>家人们!家人们!欢迎来到登录页面!</h1>
</body>
</html>
复制代码
运行程序:?
7. HttpServletRequest
7.1 HttpServletRequest 是啥?
我们通过浏览器向 Http 服务器发送了一个?Http请求协议包 , Http 服务器会自动为这个Http请求协议包 生成一个请求对象 和一个响应对象 。
而 HttpServletRequest 就代表客户端的请求,HTTP 请求中的所有信息都封装在这个对象中,我们可以使用这个对象干如下事情:
- 读取 Http 请求协议包中的请求信息
- 向 Http 服务器申请资源文件
7.2 获取请求行信息
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取请求行中【url】信息
String url = request.getRequestURL().toString();
// 2. 获取请求行中【method】信息
String method = request.getMethod();
// 3. 获取请求行中【URI】信息,URI: "/网站名/资源文件名"
String uri = request.getRequestURI();
System.out.println("请求 URL: " + url);
System.out.println("请求 method: " + method);
System.out.println("请求 URI: " + uri);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 这里用来处理用户的 post 请求
System.out.println("接收到用户的 post 请求");
}
}
复制代码
运行项目,浏览器输入:
http://localhost:8080/myProject/user
复制代码
控制台打印结果:?
7.3 获取请求头的参数信息
我们使用如下方法获取请求头的参数信息:
String parameter = request.getParameter("参数名");
复制代码
7.3.1 获取 GET 请求头的参数信息
首先我们在浏览器请求路径追加请求参数,多个参数用& 分隔,例如:
http://localhost:8080/myProject/user?name=一颗雷布斯&age=23
复制代码
修改代码:
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取请求头的 name 参数
String name = request.getParameter("name");
// 获取请求头的 age 参数
String age = request.getParameter("age");
System.out.println("name: "+name);
System.out.println("age: "+age);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 这里用来处理用户的 post 请求
System.out.println("接收到用户的 post 请求");
}
}
复制代码
运行程序,查看控制台打印信息:?
7.3.2 获取 POST 请求头的参数信息
我们先修改一下默认欢迎页面 login.html 的代码,增加一个 form 表单信息:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login.html</title>
<style>
div {
width: 300px;
height: 300px;
margin: 0 auto;
}
</style>
</head>
<body>
<div>
<form action="/myProject/user" method="post">
<lable>姓名:</lable>
<input type="text" name="name"></br>
<lable>密码:</lable>
<input type="password" name="password"></br>
<input type="submit" value="登录">
</form>
</div>
</body>
</html>
复制代码
修改 servlet 代码:
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 这里用来处理用户的 get 请求
System.out.println("接收到用户的 get 请求");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取 post 请求的 name 参数
String name = request.getParameter("name");
// 获取 post 请求的 password 参数
String password = request.getParameter("password");
System.out.println("name: "+name);
System.out.println("password: "+password);
}
}
复制代码
浏览器页面输入信息,密码是123456,点击登录按钮:?
?查看控制台打印信息:?
7.3.3 解决 POST 请求获取参数乱码问题
上面的例子中我们发现英文和数字可以正常输出,但是中文却乱码了,这是为什么?
当浏览器以 GET 方式发送请求,请求参数保存在请求头 中,Http 服务器获取请求协议包之后先对其解码。
Tomcat 负责对其解码。因为 Tomcat 默认使用utf-8 字符集,这个字符集可以解释所有国家的文字,所以即便是中文也不会乱码。
当浏览器以 POST 方式发送请求,请求参数保存在请求体 中,Http 服务器获取请求协议包之后先对其解码。
请求对象 request 负责对请求体的内容进行解码。因为 request 默认使用?ISO-8859-1 ?字符集,这个字符集是东欧语 字符集,所以遇到中文会乱码。
所以在Post 请求方式下要想解决乱码问题,应该先让请求对象使用 utf-8 字符集对请求体内容进行一次重新解码。
request.setCharacterEncoding("utf-8");
复制代码
我们重新修改一下 servlet 的代码:
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 这里用来处理用户的 get 请求
System.out.println("接收到用户的 get 请求");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Tomcat 通知请求对象,使用 utf-8 字符集对请求体内容重写解码
request.setCharacterEncoding("utf-8");
// 获取 post 请求的 name 参数
String name = request.getParameter("name");
// 获取 post 请求的 password 参数
String password = request.getParameter("password");
System.out.println("name: "+name);
System.out.println("password: "+password);
}
}
复制代码
浏览器页面输入信息,点击登录按钮查看控制台打印信息:?
8. HttpServletResponse
8.1 HttpServletRequest 是啥?
我们处理完用户的请求之后肯定要给用户做出响应,而 HttpServletResponse 就是响应对象,它能干如下事情:
- 将结果以二进制形式写入到
响应体 - 设置响应头中
content-type 属性值 - 设置响应头中
location 属性,让浏览器向指定 Http 服务器发送请求。
8.2 将响应结果发送到浏览器
我们将响应结果放到Http响应包 的响应体中,然后在浏览器对响应体的二进制内容进行编译解析,从而显示其内容。
语法格式:
PrintWriter writer = response.getWriter();
writer.println(响应结果内容);
复制代码
我们修改一下 servlet 的代码:
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String result1 = "<h1>2022: Where there is a well,there is a way<h2>";
String result2 = "海鸟跟鱼相爱,只是一场意外";
PrintWriter writer = response.getWriter();
writer.println(result1);
writer.println(result2);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 这里用来处理用户的 post 请求
System.out.println("接收到用户的 post 请求");
}
}
复制代码
运行项目,浏览器输入:
http://localhost:8080/myProject/user
复制代码
查看浏览器响应结果:?
8.3 设置响应头 content-type
从上面的例子中,我们看到英文和数字可以正常输出,但是中文乱码了,我们可以设置一下响应头 content-type 的格式,让浏览器可以输出中文。
设置 content-type 的语法格式:
response.setContentType("content-type 的格式");
复制代码
我们修改一下 servlet 的代码:
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String result1 = "<h1>2022: Where there is a well ,there is a way<h2>";
String result2 = "海鸟跟鱼相爱,只是一场意外";
// 设置响应头 content-type 的格式
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println(result1);
writer.println(result2);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 这里用来处理用户的 post 请求
System.out.println("接收到用户的 post 请求");
}
}
复制代码
运行项目,浏览器输入:
http://localhost:8080/myProject/user
复制代码
查看浏览器响应结果:?
8.4 向指定 Http 服务器发送请求
我们可以在响应头中设置 location 属性,从而让浏览器跳转到指定的网址。语法格式:
response.sendRedirect("网址");
复制代码
我们修改一下 servlet 的代码:
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 浏览器接收到 http 响应包之后,如果发现响应头中存在 location 属性
// 2. 则自动通过地址栏向 location 指定网站发送请求
// 3. sendRedirect 方法控制浏览器的请求行为
response.sendRedirect("http://www.baidu.com");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 这里用来处理用户的 post 请求
System.out.println("接收到用户的 post 请求");
}
}
复制代码
运行项目,浏览器输入:
http://localhost:8080/myProject/user
复制代码
查看浏览器响应结果:?
9. 重定向和请求转发
9.1 Servlet 之间互相访问
假如有一天你对 Http 服务器说:我想吃黄焖鸡。结果你的访问场景是这样的:
后来你彻底破防 了:“老子就想吃个黄焖鸡,需要访问这么多次?啥破网站啊,再也不用了!”
所以为了提升用户使用体验,servlet 规范定义了多个 servlet 之间互相调用的规则。
无论该请求涉及到多少个 Servlet ,用户只需要向浏览器发起一次 请求,Servlet 之间互相协作,用户得到响应结果。
多个 Servlet 之间互相调用的规则有两个:
9.2 重定向
- 原理:
用户先通过浏览器访问 oneServlet,但是 oneServlet 不能解决用户的问题。于是 oneServlet 就将 TwoServlet 的访问地址 写入到响应头 的location 属性中。
浏览器拿到 Http 响应包之后,会根据响应头中的 location 的值再次向 Http 服务器中的 twoServlet 发送请求。twoServlet 再去处理用户的请求。
其实重定向就相当于你去找刘备借钱,刘备说我没钱你去找曹操吧。然后你又去找曹操借钱,最后借到了钱。
- 特征:
- 浏览器至少发送两次请求,只有第一次是用户自己通过浏览器发送的,后面的都是浏览器自动发送的。
- 重定向请求方式是 GET 方式。
- 因为请求地址发生变化,所以浏览器地址栏会发生变化。
- 除了可以访问 http 服务器内部的资源,还可以访问外部网站资源。例如百度等。
- 浏览器多次访问服务器,增加用户等待时间。
- 实现方式
response.sendRedirect("网址");
复制代码
- 案例
新建 OneServlet
@WebServlet("/one")
public class OneServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendRedirect("/myProject/two");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
新建 TwoServlet
@WebServlet("/two")
public class TwoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("我是 twoServlet,我帮你解决问题!");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
运行项目,使用浏览器访问 OneServlet:
http://localhost:8080/myProject/one
复制代码
查看浏览器响应结果:?
9.3 请求转发
1.原理
用户先通过浏览器访问 oneServlet,但是 oneServlet 不能解决用户的问题。于是 oneServlet 将 request 对象和 response 对象传递给 TwoServlet,然后 twoServlet 去处理用户的请求。
其实请求转发就相当于你去找刘备借钱,刘备一摸兜没钱,但是刘备这人挺仗义,他就带着你去找曹操借钱,最后你从曹操那里借到了钱。?
2.特征
- 在请求转发过程中,浏览器只发送一次请求。
- 在 HTTP 服务器内部做请求转发,只能请求服务器内部资源。
- 在请求转发过程中,浏览器只发送一个 Http 请求协议包。 所以参与本次请求的所有 Servlet 共享同一个请求协议包。
- 因为在 Http 服务器内部做请求转发,所以浏览器地址栏不变。
3.实现方式
RequestDispatcher requestDispatcher = request.getRequestDispatcher("下一个servlet请求路径");
// 将本次 Http 请求协议包的【请求对象】和【响应对象】交给下一个 servlet
requestDispatcher.forward(request,response);
复制代码
4.案例
只修改 OneServlet
@WebServlet("/one")
public class OneServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 因为是http 服务器内部做请求转发,相当于调用同一个项目的资源,所以直接写 servlet的访问路径就行
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/two");
// 将本次 Http 请求协议包的【请求对象】和【响应对象】交给下一个 servlet
requestDispatcher.forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
运行项目,使用浏览器访问 OneServlet:
http://localhost:8080/myProject/one
复制代码
查看浏览器响应结果:?
?总结:重定向的重 是重新、再次的意思,也就是浏览器向服务器发送了两次以上的请求。而请求转发发生在服务器内部,浏览器只请求了一次。
10. Servlet 之间数据共享
数据共享 :第一个 Servlet 处理请求之后,将数据交给第二个 Servlet 来使用。
Servlet 规范提供了四种方式来解决数据共享问题。
10.1. ServletContext
1.全局作用域
ServletContext 对象是全局作用域对象。 全局作用域对象相当于教室里面一个公用的区域 ,老师把一些公共物品放到这里,所有同学(Servlet)都可以使用。
2.使用方法
将共享数据添加到全局作用域对象
// 1. 获取全局作用域对象
ServletContext servletContext = request.getServletContext();
// 2. 将共享数据添加到全局作用域对象
servletContext.setAttribute("key",value);
复制代码
从全局作用域对象获取共享数据
// 1. 获取全局作用域对象
ServletContext servletContext = request.getServletContext();
// 2. 从全局作用域对象获取共享数据
Object value = servletContext.getAttribute("key");
复制代码
3.案例 修改 OneServlet
@WebServlet("/one")
public class OneServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取全局作用域对象
ServletContext servletContext = request.getServletContext();
// 2. 将共享数据添加到全局作用域对象
servletContext.setAttribute("info","祝今年期末考试的你科科必过!祝今年考研、考公的你一定上岸!");
// 3. 重定向
response.sendRedirect("/myProject/two");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
修改 TwoServlet
@WebServlet("/two")
public class TwoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取全局作用域对象
ServletContext servletContext = request.getServletContext();
// 2. 从全局作用域对象获取共享数据
String info = (String)servletContext.getAttribute("info");
response.setContentType("text/html;charset=utf-8");
// 3. 将共享数据发送到浏览器
response.getWriter().println(info);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
运行项目,使用浏览器访问 OneServlet:
http://localhost:8080/myProject/one
复制代码
查看浏览器响应结果:?
10.2. Cookie
1.简介
Cookie 是 Servlet 规范中的一个对象。如果两个 Servlet 来自于同一个网站,可以使用 Cookie 对象进行数据共享。
2.原理
用户请求 OneServlet,OneServlet 创建一个 Cookie 存储与当前用户相关的数据。OneServlet 执行完毕,将 Cookie 写入到响应头交还给当前浏览器。
浏览器获取 Http 响应包之后,将 cookie 信息存储在浏览器的缓存 中。
过了一段时间,用户通过同一个浏览器访问 TwoServlet。浏览器将缓存中的 Cookie 信息写入到 Http 请求包的请求头中。
此时 TwoServlet 就可以通过读取请求头中 的cookie信息,获取 OneServlet 提供的共享数据。
其实 Cookie 的原理就像你(浏览器)第一次去健身房(Http服务器),健身房的工作人员给你办了张会员卡(Cookie)。你下次再去这家健身房,里面的工作人员拿到会员卡之后就知道你是谁了。
3.使用方式
创建 cookie,保存共享数据
// 1.创建一个 cookie 对象,保存共享数据
Cookie cookie = new Cookie("key","value");
// 2. 将 cookie 信息写入到响应头,交给浏览器
response.addCookie(cookie);
复制代码
获取共享数据
// 1. 调用请求对象从请求头得到浏览器返回的 Cookie 信息
Cookie cookies[] = request.getCookies();
// 2. 遍历数据获取 cookie 的 key 与 value
for(Cookie cookie:cookies) {
// 3. 获取 key 的值
String key = cookie.getName();
// 4. 获取 value 的值
String value = cookie.getValue();
}
复制代码
4.案例 修改 OneServlet
@WebServlet("/one")
public class OneServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.创建一个 cookie 对象,保存共享数据
Cookie cookie = new Cookie("name","jay");
// 2. 将 cookie 信息写入到响应头,交给浏览器
response.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
修改 TwoServlet
@WebServlet("/two")
public class TwoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 调用请求对象从请求头得到浏览器返回的 Cookie 信息
Cookie cookies[] = request.getCookies();
// 2. 遍历数据获取 cookie 的 key 与 value
for(Cookie cookie:cookies) {
// 3. 获取 key 的值
String key = cookie.getName();
// 4. 获取 value 的值
String value = cookie.getValue();
System.out.println("key: "+key+" value: "+value);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
运行项目,先访问 OneServlet:
http://localhost:8080/myProject/one
复制代码
再访问 TwoServlet:
http://localhost:8080/myProject/two
复制代码
查看控制台打印结果:
注意:
- cookie 相当于一个 map。
- 一个 cookie 中只能存放一个键值对。
- 键值对的 key 与 value 只能是 String 类型。
- 键值对中 key 不能是中文。
5.cookie 的销毁时机
默认情况下,Cookie 对象保存在浏览器的缓存中。所以只要关闭浏览器,Cookie 对象就会被销毁。
为了延长 Cookie 的存活时间,我们可以将 Cookie 信息存储在本地计算机的硬盘上,并设置 Cookie 的存活时间。
在存活时间范围内,关闭浏览器或者服务器,都不会导致 Cookie 被销毁。当存活时间时到了,Cookie 自动被删除。
设置 Cookie 在硬盘存活时间:
Cookie cookie = new Cookie("name","jay");
//cookie 在硬盘上存活 2 分钟
cookie.setMaxAge(120);
复制代码
10.3. Session
1.简介
Session ?是 Servlet 规范中的一个对象。如果两个 Servlet 来自于同一个网站,可以使用 Session 对象进行数据共享。我们习惯将 Session 叫做会话对象。
2.原理
用户请求 OneServlet,OneServlet 创建一个 Session 对象存储与当前用户相关的数据。OneServlet 执行完毕,将 Session 保存到 Http 服务器的内存中。
然后用户通过同一个浏览器访问 TwoServlet。TwoServlet 就可以通过Http 服务器内存中的 Session,获取 OneServlet 提供的共享数据。
其实 Session 可以看做服务器端的保险柜。
3.使用方式
创建 Session,保存共享数据
// 1. 创建 session 对象
HttpSession session = request.getSession();
// 2. 将数据保存到服务器内存中(保险柜)
session.setAttribute("key",共享数据);
复制代码
获取共享数据
// 1. 获取 session
HttpSession session = request.getSession();
// 2. 从 session 中获取共享数据
Object 共享数据 = session.getAttribute("key");
复制代码
4.案例
修改 OneServlet
@WebServlet("/one")
public class OneServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 创建 session 对象
HttpSession session = request.getSession();
// 2. 将数据保存到服务器内存中(保险柜)
session.setAttribute("key",123);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
修改 TwoServlet
@WebServlet("/two")
public class TwoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取 session
HttpSession session = request.getSession();
// 2. 从 session 中获取共享数据
Object info = session.getAttribute("key");
System.out.println("从session中获取信息:"+info);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
运行项目,先访问 OneServlet:
http://localhost:8080/myProject/one
复制代码
再访问 TwoServlet:
http://localhost:8080/myProject/two
复制代码
查看控制台打印结果:?
?注意:
- HttpSession 对象可以存储任意类型的共享数据。
- HttpSession 相当于一个 map集合,所以可以存储任意数量共享数据。
10.4. HttpServletRequest
1.简介
在同一个网站中,两个 Servlet 之间通过请求转发 方式进行调用, 因为他们共享同一个请求协议包,所以 servlet 之间共享同一个请求对象。因此可以利用这个请求对象在两个 Servlet 之间实现数据共享。
这个请求对象叫做请求作用域对象 。
2.使用方式
使用请求对象保存共享数据
// 数据类型可以是任意类型
req.setAttribute("key",数据);
复制代码
使用请求对象获取共享数据
// 从请求对象获取共享数据
Object 数据 = req.getAttribute("key");
复制代码
3.案例
修改 OneServlet
@WebServlet("/one")
public class OneServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 使用请求对象保存共享数据
request.setAttribute("info","我一路向北,离开有你的季节。");
// 请求转发
request.getRequestDispatcher("/two").forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
修改 TwoServlet
@WebServlet("/two")
public class TwoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 从请求对象中获取共享数据
Object info = request.getAttribute("info");
response.setContentType("text/html;charset=utf-8");
response.getWriter().println(info);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
复制代码
运行项目,使用浏览器访问 OneServlet:
http://localhost:8080/myProject/one
复制代码
查看浏览器响应结果:?
11. Filter 过滤器
1.简介
Filter 是 Servlet 规范下的一个接口,主要用来告诉 Tomcat 在调用某一个资源文件之前,对用户的请求进行拦截 。
比如用户还没有登录网站就想查看自己购物车的信息,这时候就需要对用户的请求进行拦截。?
2.使用方式
- 新建 filter 类,实现 Filter 接口中的 doFilter 方法。
- 在 web.xml 中配置 filter 类
<!--配置过滤器-->
<filter>
<filter-name>userFilter</filter-name>
<filter-class>com.xxl.filter.UserFilter</filter-class>
</filter>
<!-- 告诉 Tomcat 在用户调用何种资源文件时需要被当前过滤器拦截-->
<filter-mapping>
<filter-name>userFilter</filter-name>
<url-pattern>拦截路径</url-pattern>
</filter-mapping>
复制代码
3.案例
新建 UserFilter
public class UserFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
}
}
复制代码
在项目中放一个美女照片:?
?在 web.xml 中配置 UserFilter,拦截该照片。
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--配置过滤器-->
<filter>
<filter-name>userFilter</filter-name>
<filter-class>com.xxl.filter.UserFilter</filter-class>
</filter>
<!-- 告诉 Tomcat 在用户调用何种资源文件时需要被当前过滤器拦截-->
<filter-mapping>
<filter-name>userFilter</filter-name>
<url-pattern>/girl.jpg</url-pattern>
</filter-mapping>
</web-app>
复制代码
完善 UserFilter 代码,判断 age 参数是否大于 18 岁。
public class UserFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 1. 通过【拦截请求对象】获取请求参数信息
String age = servletRequest.getParameter("age");
// 2. 如果年龄合法
if (Integer.parseInt(age) > 18) {
// 3. 将拦截请求对象和响应对象交还给 Tomcat,由 Tomcat 继续调用资源文件,放行
filterChain.doFilter(servletRequest,servletResponse);
}else{
servletResponse.setContentType("text/html;charset=utf-8");
servletResponse.getWriter().println("看啥美女!写你的作业去!");
}
}
}
复制代码
运行结果:?
4.注解式开发
不需要在 web.xml 中配置,直接在 filter 类上加注解:
@WebFilter(filterName = "filter名字",urlPatterns = "拦截路径")
复制代码
修改 UserFilter 代码:
@WebFilter(filterName = "userFilter",urlPatterns = "/girl.jpg")
public class UserFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 1. 通过【拦截请求对象】获取请求参数信息
String age = servletRequest.getParameter("age");
// 2. 如果年龄合法
if (Integer.parseInt(age) > 18) {
// 3. 将拦截请求对象和响应对象交还给 Tomcat,由 Tomcat 继续调用资源文件,放行
filterChain.doFilter(servletRequest,servletResponse);
}else{
servletResponse.setContentType("text/html;charset=utf-8");
servletResponse.getWriter().println("看啥美女!写你的作业去!");
}
}
}
复制代码
运行效果一样。
|