文章目录
- 一、HTTPServlet
- 介绍其中的关键 三个方法
- 二、HTTPServletRequest(处理请求)
- 1.分块介绍方法作用
- get 为前缀的方法
- 字段中 含有 getParameter 字段 的方法(前后端交互):
- 字段中 含有 getHeader 字段 的方法:
- 2.解释前后端的交互过程
- 3.使用 json 格式创建键值对结构
- 4.使用第三方库 Jackson 构建
- 三、HTTPServletResponse(处理响应)
- 解释其中的部分方法
- 第一组
- 第二组
- sendRedirec 方法
- 四、再次阐述 cookie 和 session
- 1、什么是 cookie 以及登录的基本流程
- 2、了解 cookie 和 session 的联系与区别
- 3.使用代码简单解释 cookie 和 session 的工作原理
- 4.总结
一、HTTPServlet
在我们写 Servlet 的相关代码时。在创建类时,都会继承 HTTPServlet 后重写其中的某些方法。
核心方法如下所示:
介绍其中的关键 三个方法
这三个方法分别是:
init、service、destory
(doGet、doPost 等方法在前面的文章中已经简单的使用过,这里就不在描述了)
- init 方法
在 HTTPServlet 实例化后就被调用一次。
也就是说 Tomcat 在首次收到和该类相关联的 请求 时就会返回 (类似于 “懒汉模式”)
实现 init 方法如下:
需要注意的是,实例化的进行只进行一次。
这里通过多次访问页面观察情况:
很显然,当后续在收到请求时,就不会重复进行实例化了。
- destory 方法
当 HTTPServlet 实例不在使用时就会调用。
实现 destory 方法:
当关闭服务器时,就会调用这个方法:
注:这里的 destory 方法能不能被执行到,是有待商榷的。
- 如果通过 Smart Tomcat 的 停止按钮,这个操作本质上就是通过 Tomcat 的 8005 端口,主动停止,就可以触发 destory。
- 如果直接杀进程,此时就可能会来不及执行 destory 就结束了。
- 结合三者介绍 生命周期
所谓生命周期,就是一个事物,从什么时候开始,什么阶段做什么事,什么时候结束。。。
面试题:Servlet 的生命周期是怎么回事?
对于 Servlet :
1.开始的时候,执行 init
2.每次收到请求时,执行 service
3.销毁之前,执行 destory
二、HTTPServletRequest(处理请求)
当 Tomcat 通过 SocketAPI 读取 HTTP 请求时,就会按照 HTTP 协议的格式将字符串解析为 HTTPServletRequest 的形式。
核心方法如下所示:
1.分块介绍方法作用
本人简单的对上面的方法进行了简单的分类,这样就比较便于识别,下面我会简单的进行解释
get 为前缀的方法
这些方法主要的作用就是 获取 HTTP 的内部格式信息,这里通过简单的代码进行演示:
运行结果展示:
字段中 含有 getParameter 字段 的方法(前后端交互):
用来获取 query string 的键值对结构,这些方法是 通过 key 来获取 Value。
这些键值对内容是什么完全是由程序员自定义的。
通过代码来解释 getParameter 的使用:
- 在 GET 请求的情况下
在前端向后端传输信息时:
这里创建出两个变量作为 key
我们在访问时,提供两个相应的值时,就会在前端展现出来 (此时,如果没有输入值返回值就是 null),如图:
- 在 POST 请求的情况下
在前端中为 from 表单的数据向后端传输 信息时:
对于这种情况,后端使用的方法仍然是 getParameter
同样的这里仍然使用上面的两个变量。
使用 from 表单构造一个前端页面和后端进行交互。
输入信息后点击 “提交”,此时使用 Fiddle 进行抓包就会获得的到下面的结果:
注:
这里在构造好 html 文件后,切记要将文件放到 webapp 根目录下
访问时的路径方式为:
字段中 含有 getHeader 字段 的方法:
就是获取 HTTP 请求中请求头的信息。
代码展示:
此时,在网页中进行访问,就会在页面上获取到下面的信息:
在这里,如果使用 Fiddle 抓包,也会获得相同的信息。
2.解释前后端的交互过程
这里始终记住一点,此时的前端文件是存放在 webapp 这个文件夹下的。其中的请求,是可以被后端的代码解析到的。
这里通过 from 表单构造的前端页面与后端页面的交互进行简单解释。
前端代码(构造出一个前端请求):
<body>
<form action="postParameter" method="post">
<input type="text" name="studentID">
<input type="text" name="classID">
<input type="submit" value="提交">
</form>
</body>
后端代码:
//获取请求时的最后一级路径
@WebServlet("/postParameter")
public class PostParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String studentID = req.getParameter("studentID");
String classID = req.getParameter("classID");
//告诉前端页面,这里的 body 的类型格式是什么样的
resp.setContentType("text/html");
//将获取到的信息输出到前端页面
resp.getWriter().write("studentID = " + studentID + "classID = " + classID);
}
}
将这里的四部分信息进行简单整合,大致如下图:
-
前端代码 ——> 前端页面
通过 from 表单构造出一个 post 请求。 -
前端页面 ——> 后端接收(Tomcat 服务器)
Tomcat 通过前端的信息构造出 req 和 resp 对象。
其中的值存储在 req 对象中,通过 HTTPServletRequest 中的 getParameter 方法调用其中的值。 -
后端 ——> 前端(浏览器)
最后通过 resp 编写响应,写回到 Tomcat 中,并返回给浏览器显示。
形象图解:
3.使用 json 格式创建键值对结构
首先 ,json 的数据格式如下图所示:
上面就是将 body 按照这个方式进行了组织。
前端,可以通过 ajax 的方式进行构造,但是在这里,方便起见就是使用 postman 直接构造。
后端代码:
@WebServlet("/postParameter2")
public class PostParameterServlet2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//这个方法来处理 body 为 json 格式的数据
//直接将 req 中对象的 body 完整的读取出来
int length = req.getContentLength();
//将获取到的字符长度赋给这个数组
byte[] buffer = new byte[length];
//定义一个输入流,并且将数据存储下来
InputStream inputStream = req.getInputStream();
//读取字符串信息
inputStream.read(buffer);
//将这个字符数组构造成一个 String 类型的字符串,并打印在运行结果中
String body = new String(buffer,0,length,"utf8");
System.out.println("body: " + body);
resp.getWriter().write(body);
}
如图,使用 postman 构造一个 json 格式字符串:
发送 post 请求后,就会在运行结果中出现拼接后的字符串结果。
4.使用第三方库 Jackson 构建
在上面的方法中,我们使用了 json 格式传递数据。但是,服务器这边只是将整个 body 读取出来,并没有按照键值对的方式进行处理(还不可以根据 key 获取 Value)
对于上面的问题,使用 Jackson 就可以解决。
-
通过 maven 引入第三方库
搜索 Jackson 后,第一个应该就是我们需要使用的第三方库。
-
将找到的 .xml 片段复制到 pom.xml 中
-
通过代码实现 Jackson 操作
这里的操作会简单很多,使用一行代码就可以完成。
事先准备:
这个代码的实现方式比较特殊,需要一个事先准备好的一个 class 类确定传输的 key 值
class Student{
public int studentID;
public int classID;
}
Jackson 实现数据读取
@WebServlet("/postParameter2")
public class PostParameterServlet2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//使用 Jackson 的方式实现代码
ObjectMapper objectMapper = new ObjectMapper();
//readValue 就是将一个 json 字符串转成一个 Java 对象
Student student = objectMapper.readValue(req.getInputStream(),Student.class);
System.out.println(student.studentID + ", " + student.classID);
}
- 运行结果展示
在发送一个请求后,这里仍然会在运行窗口进行打印,如图:
很明显,这里就只是出现了单纯的 Value 值。
这里解释一下 Jackson 的运行流程
在这里,关键的代码就只有一行:
- 从 body 中读取出 json 格式字符串
- 根据 第二个参数类对象,构建 Student 实例
- 解析上面的 json 格式字符串,处理为键值对结构。
- 遍历所有键值对,与 Student 实例中的元素进行比对,将合适的 Value 设置到该属性中。
- 返回实例。
三、HTTPServletResponse(处理响应)
上面描述方法,就是根据请求计算响应。之后将响应传递给 Response 内的对象中
Tomcat 就会将这里 Response 对象按照 HTTP 协议的格式,转成字符串,通过 Socket 写回给浏览器。
核心方法
如上图所示,我将比较重要且相似的方法已经标记出来。
解释其中的部分方法
第一组
这一组的方法 都与 header 有关。
两个不同的方法,针对添加 header 的键值对的原则不同。
第二组
-
解释 setContentType
这个方法在前面的代码中我们已经使用过了。
其目的是,告诉浏览器这里的 body 是什么类型的文件,以什么形式进行展示。 -
解释 setCharacterEncoding
这个方法主要是告诉浏览器,这个页面的编码字符集是什么样的。
这里通过代码来简单演示一下其作用:
未设置字符集
@WebServlet("/SetCharacter")
public class TestSetCharacterEncoding extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String studentID = req.getParameter("studentID");
String classID = req.getParameter("classID");
resp.setContentType("text/html");
resp.getWriter().write("学生ID =" + studentID + "班级ID =" + classID);
}
设置字符集
//添加设置字符集
resp.setCharacterEncoding("utf8");
sendRedirec 方法
这个方法是构造一个重定向响应。
也就是 3XX 的状态码,浏览器会自动跳转到指定的新地址。
同样的,这里通过简单的代码进行验证:
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("https://www.baidu.com");
}
}
使用 Fiddle 进行抓包操作,就会获得下面的信息,重定向实现了。
四、再次阐述 cookie 和 session
这里的 cookie 在本人前面的文章 HTTP协议解释(上篇) 中已经有所简单解释。
在这里,本人会以更加详细的形式对这两个重点信息进行解释。
1、什么是 cookie 以及登录的基本流程
要了解什么是 cookie 首先,我们需要明白下面几个问题:
- cookie 是一个什么东西?
这是一个 浏览器 提供的一个持久化存储数据 (例如登录某些网页时的账号信息) 的机制。- cookie 存储在哪里?
这些信息会存储在客户端的主机硬盘中。根据不同的网站域名,浏览器会分别存储。- cookie 从什么地方来的?
cookie 是从 服务器返回给浏览器的 。
在服务器这边,程序员决定要将什么信息保存到客户端这边。- cookie 最终会回到哪里去?
cookie 中的信息最终会在浏览器访问服务器时,将信息带到请求头 header 中一并传输给服务器。
对于上面的文字解释是比较晦涩的,下面我通过一个最常用的例子来更加详细的解释 cookie 的工作过程:
对于 cookie ,最典型的应用就是标识用户的身份信息,也就是网站的登录功能。
如图:
针对登录操作: QQ 的服务器会通过数据库来判断用户的身份信息。
登陆成功: 此时,服务器就会将身份信息保存一份。并且向客户端分配一个 唯一的 身份序号。 这个身份序号 就存储在 cookie 中。(这个唯一的身份序号就被称之为 sessionID)
session (会话): 服务器会像 hash 这样的表结构一样,将 序号 作为 key,身份信息 作为 value 存储。
后续请求: 在客户端发出请求时,会同时将 cookie 中的身份序号发送给服务器。此时,服务器就会查询上述的 hash 表,判定用户的身份信息。
这里还有一个设定就是 “过期”。有两种可能。
一种是客户端将 cookie 删除了。
一种是服务器将对应的 身份信息 删除了。
这就运用在了比较敏感的一些网站上。
2、了解 cookie 和 session 的联系与区别
- 联系: 在网站登录的功能中,两者需要共同的配合来使用。
- 区别:
(1)cookie 可以单独进行使用,不搭配 session (实现非登录的情况)
(2)session 也可以不搭配 cookie 使用。(例如手机 app 进行登录就需要 session 此时就没有 cookie 的概念)cookie 是 与浏览器强相关的。
(3)cookie 是 客户端 的存储机制,session 是 服务器 的存储机制。
(4)cookie 中可以存储各种键值对,session 则是专门用来保存用户的身份信息。
(5)cookie 是 HTTP 协议中的一个部分。
session 则可以与 HTTP 协议无关。
3.使用代码简单解释 cookie 和 session 的工作原理
要解释这两个的工作原理,可以通过一个简单的 模拟登陆页面 进行解释。
涉及到的东西大致为下面四个部分:
两个页面:
1.登录页面
2.主页面
两个 servlet:
1.处理的登录请求的 LoginServlet 判定用户名和密码。
2.构建主页面的 IndexServlet
流程图如下:
- 编写前端登录页面
使用 from 表单就可以编写出一个登录请求页面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录页面</title>
</head>
<body>
<form action="login" method="post">
<input type="text" name="username">
<br>
<input type="password" name="password">
<br>
<input type="submit" value="登陆">
</form>
</body>
</html>
通过抓包我们可以获取到下面各个元素之间的联系:
- 编写 LoginServlet 处理登录请求
简单思路如下:
(1)首先,我们通过前端的代码已经知道会发出一个 post 请求。用户的 信息 就存在于 post 请求的 body 中。
(2)对此,后端首先会获取前端 body 中的信息。在于后端中的信息核对(这里方便期间就先写死了数据)
(3)最后会创建出一个新的 session 会话。将 用户名 信息进行保存,并重定向到主页面。
代码如下(详细解释已经在代码注释中):
package login;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取到前端页面 body 中的用户名以及密码信息
String username = req.getParameter("username");
String password = req.getParameter("password");
// 在获取到相应信息后,就需要对信息进行核对。
// 正常情况下是需要数据库进行存储信息,此处为了方便就直接将用户信息写死
// 这里约定用户有 zhangsan 和 lisi
// 密码都为 123
if(!username.equals("zhangsan") && !username.equals("lisi")){
//登录失败
//后端反馈登录失败信息
System.out.println("登录失败,请重新登录");
//进行页面的重定向,将页面返回到登录页
resp.sendRedirect("login.html");
return;
}
if(!password.equals("123")){
//登录失败
//后端反馈登录失败信息
System.out.println("登录失败,请重新登录");
//进行页面的重定向,将页面返回到登录页
resp.sendRedirect("login.html");
return;
}
//登陆成功
// 1. 创建出一个会话
// 这里设定的 true 是为了判读是否需要创建一个新的 会话
HttpSession session = req.getSession(true);
// 2,将当前用户的用户名保存到会话中
session.setAttribute("username",username);
// 3,重定向到主页面
resp.sendRedirect("index");
}
}
解释 Session 方法的工作原理
session (会话) 是一个键值对。
key 是 sessionID
value 是 HttpSession 对象
问题一:
这里设定为 true ,就会先判定当前请求是否已经有对应的会话了。(就是拿请求中的 cookie 中的 SessionID 来查哈希表)
此时,若 SessionID不存在,就会创建新的会话。并插入到哈希表。
如果 查到了 就直接返回查到的结果。
创建过程:
问题二:
这里的 setAttribute 、getAttribute 就是来存取键值对。这里的键值对内容由程序员自己定义。
在服务器中,会话的组织形式如图所示:
- 编写 IndexServlet 生成主页
简单思路如下:
(1)首先获取当前用户的登录情况。
(2)获取到前面绘画中的 用户名 信息
(3)在页面中输出信息
代码如下:(同样的,详细解释已经在代码中注释出来)
package login;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
//通过重定向,浏览器发送的会是一个 get 请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 首先判断用户的登录状态
// 如果还没有登录,就请求先登录
// 已经登录了,就根据当前会话中的用户名,显示到页面上
//首先获取当的会话,并不创建新的会话
// 如果会话存在,则表明当前的用户已经登录了
// 如果会话不存在,则表名当前用户处于未登录状态
HttpSession session = req.getSession(false);
if(session == null){
//服务器信息反馈
System.out.println("用户未登录");
// 此时进行重定向,定向到登录页面
resp.sendRedirect("login.html");
return;
}
// 从前面的会话中获取到用户信息
String username = (String) session.getAttribute("username");
// 构造一个页面
// 设定页面输出的格式
resp.setContentType("text/html;charset=utf8");
// 在页面输出信息
resp.getWriter().write("欢迎 "+ username + " 登录!");
}
}
问题一:
结合前面的代码。
登录页面最后的重定向,就定向到了当前的页面中。
4.总结
对于上述的完整代码,本人已经放置到码云之中 cookie&Session
最后,我们通过抓包工具,就可以很明确的了解到整个代码的运行逻辑,如下图:
在完成一次登陆之后,后续多次请求服务器,都会带上这了 cookie 的值 (也就是 SessionID)。
码子不易,您小小的点赞是对我最大的鼓励!!!