整合多方大佬博客以及视频 一文读懂 servlet
参考文章以及视频
文章:
都2023年了,Servlet还有必要学习吗?一文带你快速了解Servlet_servlet用得多吗-CSDN博客
【计算机网络】HTTP 协议详解_3.简述浏览器请求一个网址的过程中用到的网络协议,以及协议的用途(写关键点即可)-CSDN博客
【从理论到应用】HTTP请求响应详解 (请求数据格式,请求方式,Web开发中的体现)_apifox 如何转换htt请求-CSDN博客
HTTP响应是什么?_响应内容-CSDN博客
Servlet的生命周期-CSDN博客
HttpServletRequest介绍和使用-CSDN博客
【Servlet】请求转发与重定向_servlet请求转发可以访问其它应用的资源吗-CSDN博客
https://blog.csdn.net/qq_41264674/article/details/80472666
cookie详解-CSDN博客
什么是Session的销毁方式?_销毁session-CSDN博客
ServletContext详解-CSDN博客
【JavaWeb】会话管理 cookie session 三大域对象总结_cookie登录时效性-CSDN博客
视频:
【可能是B站讲的最好的Servlet教程,一天打通Servlet全套教程丨2022最新版,轻松掌握servlet基础+案例实操】https://www.bilibili.com/video/BV1kt4y157xd?p=15&vd_source=bb412cc25ca27e171f8e17085daad038
1、主旨大纲
2、Http协议
介绍
浏览器的书写格式
Http之url
Http请求
介绍
HTTP请求格式是由三部分组成:
请求行(Request line)
:包括请求方法、URL和协议版本。
- 请求方法(Request method):表示要执行的操作,常见的方法有GET、POST、PUT、DELETE等。
- URL(Uniform Resource Locator):表示要访问的资源路径。
- 协议版本(Protocol version):表示使用的HTTP协议版本,如HTTP/1.1。
请求头部(Request headers)
:包括一些关于请求的额外信息,如User-Agent、Content-Type、Authorization等。
请求体(Request body)
:用于传输请求的数据,对于GET请求来说,请求体通常为空。
示例
以下是一个示例HTTP请求的格式:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110
Safari/537.3
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
在这个示例中:
请求行包括GET方法、URL为/index.html和协议版本为HTTP/1.1。
请求头部包括Host、User-Agent、Accept、Accept-Encoding和Accept-Language等字段。
请求体为空,因为这是一个GET请求。
在上述实例中,申明了该请求是GET请求,我们最常用的请求方式就是GET和POST请求,二者在请求参数的传递中有很大不同,正如上述实例中所说:
GET请求的请求参数在请求行中,没有请求体
POST请求的请求参数在请求体中
格式
Http响应
介绍
当客户端发起一个请求后,一般都会得到一个服务器的响应,断网或者服务器宕机的情况下除外。服务器发送给客户端的 HTTP 响应用于向客户端提供其请求的资源,以及客户端请求的执行结果。
与请求类似,HTTP 响应同样由四个部分组成,分别为响应行(状态行)、响应头、空行和响应体,如下图所示:
响应行
响应行以 HTTP 协议版本
、表示响应状态的状态码
和形容这个状态的一个短语组成
,每个部分使用空格分隔,如下所示:
HTTP/1.1 200 OK
其中,HTTP/1.1 为 HTTP 协议版本,200 为响应的状态码,OK 为状态文本。注意:响应行中的字母都是大写的。
HTTP 响应的状态码是一个三位的整数,其中状态码的第一位用来表示响应的类别,状态码一共有 5 类,如下表所示;
响应头
响应头与 HTTP 请求中的请求头类似,同样由头部字段名、冒号、空格和值组成,例如Date: Tue, 22 Sep 2020 02:00:55 GMT。响应头中包含了一系列服务器的信息,以及服务器对请求的响应。
HTTP 协议的响应头中常用的头部字段名以及含义如下表所示:
空行与响应体
与 HTTP 请求中的空行相同,HTTP 响应中同样使用空行来表示响应头结束。响应体则是服务器根据客户端的请求返回给客户端的具体数据。
示例
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache/2.2.14 (Win32)
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
Content-Length: 88
Content-Type: text/html
Connection: Closed
<html>
<body>
<h1>Hello, CSDN!</h1>
</body>
</html>
3、Servlet
介绍
Servlet 是 JavaEE 的规范之一,通俗的来说就是 Java 接口,将来我们可以定义 Java 类来实现这个接口,并由 Web 服务器运行 Servlet ,所以 TomCat 又被称作 Servlet 容器。
Servlet 提供了动态 Web 资源开发技术,一种可以将网页数据提交到 Java 代码,并且将 Java 程序的数据返回给网页的技术,使用 Servlet 技术实现了不同用户登录之后在页面上动态的显示不同内容等的功能。
简单实现Servlet
@WebServlet("/servlet01")
public class servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest rep, HttpServletResponse resp) throws IOException {
System.out.println("hello servlet");
resp.getWriter().write("hello servlet?");
}
}
- 1、创建普通的类
- 2、继承HttpServlet
- 3、重写service方法,用来处理请求
- 4、设置@WebServlet注解,指定访问路径
Servlet的工作流程
Servlet的实现方式
第一种:继承HttpServlet
@WebServlet("/servlet01")
public class servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest rep, HttpServletResponse resp) throws IOException {
System.out.println("hello servlet");
resp.getWriter().write("hello servlet?");
}
}
第二种:继承GenericServlet
@WebServlet("/serlet02")
public class servlet02 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
}
}
第三种:实现Servlet接口
补充:
除了去实现相应的service方法
,实际上我们还可以直接去使用doget
,dopost
等方法
我们查看Httpservice的源码,看看它的service方法
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);<---------------------------------------------
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);<--------------------------------------
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);<-------------------------------
} else if (method.equals("POST")) {
this.doPost(req, resp);<--------------------------------------
} else if (method.equals("PUT")) {
this.doPut(req, resp);<---------------------------------------
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);<----------------------------------
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);<------------------------------------
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);<-------------------------------------
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);<----------------------------------
}
}
我打标记,这里发现,实质就是先去获取你是什么请求方法,然后去调用对应的doget
,dopost
等方法
Servlet的生命周期
介绍
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- Servlet 初始化后调用 init () 方法。
- Servlet 调用 service() 方法来处理客户端的请求。
- Servlet 销毁前调用 destroy() 方法。
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
架构图:
下图显示了一个典型的 Servlet 生命周期方案。
-
第一个到达服务器的 HTTP 请求被委派到 Servlet 容器。
-
Servlet 容器在调用 service() 方法之前加载 Servlet。
-
然后 Servlet 容器处理由多个线程产生的多个请求,每个线程执行一个单一的 Servlet 实例的 service() 方法。
init() 方法
init 方法被设计成只调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。因此,它是用于一次性初始化,就像 Applet 的 init 方法一样。
Servlet 创建于用户第一次调用对应于该 Servlet 的 URL 时,但是您也可以指定 Servlet 在服务器第一次启动时被加载。
当用户调用一个 Servlet 时,就会创建一个 Servlet 实例,每一个用户请求都会产生一个新的线程,适当的时候移交给 doGet 或 doPost 方法。init() 方法简单地创建或加载一些数据,这些数据将被用于 Servlet 的整个生命周期。
init 方法的定义如下:
public void init() throws ServletException {
// 初始化代码...
}
service() 方法
service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。
下面是该方法的特征:
public void service(ServletRequest request,
ServletResponse response)
throws ServletException, IOException{
}
service() 方法由容器调用,service 方法在适当的时候调用 doGet、doPost、doPut、doDelete 等方法。所以,您不用对 service() 方法做任何动作,您只需要根据来自客户端的请求类型来重写 doGet() 或 doPost() 即可。
doGet() 和 doPost() 方法是每次服务请求中最常用的方法。下面是这两种方法的特征。
doGet() 方法
GET 请求来自于一个 URL 的正常请求,或者来自于一个未指定 METHOD 的 HTML 表单,它由 doGet() 方法处理。
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Servlet 代码
}
doPost() 方法
POST 请求来自于一个特别指定了 METHOD 为 POST 的 HTML 表单,它由 doPost() 方法处理。
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Servlet 代码
}
destroy() 方法
destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法可以让您的 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收。destroy 方法定义如下所示:
public void destroy() {
// 终止化代码...
}
补充:tomocat与servlet工作的时序
HttpservletRequest
介绍:
httpServletRequest对象对http请求进行了封装,我们使用这个对象就可以方便快捷的获取http中的信息。HttpServletRequest 对象代表客户端的请求,在里面就包含了所有的客服端信息,我们通过这个对象的方法便可以获取所有的客服端信息,包括请求头和请求体中的所有数据。
常见方法:
getRequestURL()
:得到请求的URIgetRequestURL()
:得到请求的URLgetHeader(String s)
:getHeader表示得到请求头,参数s表示想要获取请求头中的什么数据getHeader("Host")
:获取请求的主机getHeader("Referer")
:获取请求来自于哪里,一般用来做防盗链getRemotetAddr()
:得到请求的ipgetParameter(String s)
:得到请求参数的值getParameterValues(String s)
:得到请求参数的值,用于参数有多个值,返回一个数组
案例演示:
前端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="http://localhost:8080/servlet/requestTest" method="post">
用户名:<input type="text" name="username"> <br>
密 码:<input type="password" name="password"><br>
爱好 <input type="checkbox" name="hobby" value="lq">篮球
<input type="checkbox" name="hobby" value="zq">足球
<input type="checkbox" name="hobby" value="pq">排球<br>
<input type="submit" value="注册">
</form>
</body>
</html>
后端:
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Arrays;
@WebServlet("/requestTest")
public class HttpServletRequestTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//得到请求URL
StringBuffer requestURL = request.getRequestURL();
System.out.println("请求的URL是:" + requestURL);
//得到请求URI
String requestURI = request.getRequestURI();
System.out.println("请求的URI是:" + requestURI);
//得到主机名Host
String host = request.getHeader("Host");
System.out.println("请求的主机名是:" + host);
//得到请求的ip
String ip = request.getRemoteAddr();
System.out.println(ip);
//得到请求参数,分别得到用户名,密码还有爱好
//如果参数中有中文,需要设置编码
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
String password = request.getParameter("password");
String[] hobbies = request.getParameterValues("hobby");
System.out.println("用户名为:" + username);
System.out.println("密码:" + password);
System.out.println("爱好为:" + Arrays.toString(hobbies));
//得到访问来自哪里
String referer = request.getHeader("Referer");
System.out.println("请求来自于:" + referer);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
发送请求
抓包
请求头:
POST /servlet/requestTest HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 60
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/servlet/register_request.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=ABEC5EA43080A0BFA70C2AABDF39BB7C; Pycharm-cb145b54=bac13d99-2d7f-4b5f-9c85-e783571ee72d; Idea-8cd34e8b=1bf6d6e0-14ee-4950-96ec-48105743bb40
请求体:
控制台输出:
请求转发
介绍
request作用域(实际就是service方法里面的HttpServletRequest rep)
@WebServlet("/s05")
public class servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest rep, HttpServletResponse resp) throws IOException, ServletException {
System.out.println("Servlet05");
rep.setAttribute("name","admin");
rep.getRequestDispatcher("s06").forward(rep,resp);
}
}
@WebServlet("/s06")
public class servlet04 extends HttpServlet {
@Override
protected void service(HttpServletRequest rep, HttpServletResponse resp) throws IOException {
System.out.println("Servlet06");
String name =(String) rep.getAttribute("name");
System.out.println("name"+name);
}
}
当我们访问/s05
时,请求转发会去跳转到/s06
,/s06
获取到/s05
的传送过来的rep里面的name对应的数据
HttpServletResponse
介绍
响应数据
响应字符数据
通过response对象,将你想要响应到客户端的字符型数据发送到浏览器里面。只用两步就可以实现:1、先通过response对象获取字符输出流:PrintWriter writer = resp.getWriter();2、将你想要写的数据通过write()方法响应到客户端:writer.write()。
public class HttpServletResponse001 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.write("hello servlet!!!");
}
}
需要注意的几点:
1.response不仅可以返回纯文本,还可以HTML的各个标签。在返回标签时要注意先把响应响应头的ContentType属性改成text/html格式,不然浏览器会默认把相应的内容当成纯文本解析。
public class HttpServletResponse001 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.write("hello servlet!!!");
writer.write("<br/>name:<input type = 'text'/>");
}
}
2.如果响应的是中文的内容,需要把响应头字体类型改成UTF-8。因为服务器默认的字体类型是ISO-8859-1,如果不修改会导致浏览器解析乱码。
public class HttpServletResponse001 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// resp.setContentType("text/html");
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.write("我的servlet程序!!!");
writer.write("<br/>姓名:<input type = 'text'/>");
}
}
3.修改响应头等信息需要在通过response对象获取字符输出流就完成,在后面才修改没有效果。
4.响应结束后,相应的字符流不需要关闭。他不需要向像Java基础里面字节输入流那种最后需要手动关闭流节省资源。因为在响应结束后,response对象自动销毁,服务器会对其关闭。
响应字节数据
通过response对象,将你想要响应到客户端的字符型数据发送到浏览器里面。大致需要3步完成:1、读取文件(图片,音频,视频等二进制输入输出数据)2、获取字节输出流3、通过write()方法响应给客户端
public class HttpServletResponse002 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.读物文件
FileInputStream inputStream = new FileInputStream("C:\Users\cx\Desktop\1.png");
//2.获取字节输出流
ServletOutputStream outputStream = resp.getOutputStream();
//3.打印文件
byte []b = new byte[2048];
int len = 0;
while ((len = inputStream.read(b))!=0){
outputStream.write(b,0,len);
}
inputStream.close();
}
}
补充:响应乱码问题
重定向
介绍
注:通常服务器发送一个302状态码以及一个Location消息头(该消息头的值是一个地址,即重定向地址),浏览器收到之后,会立即向重定向地址发送请求。
如何重定向
response.sendRedirect(String url)。
注:url即重定向地址。
重定向与请求转发
重定向:
请求转发:
不同:
Cookie
介绍:
cookie用户标记用户信息,现代浏览器有时会有这样的功能,当我们第一次登录某个系统后,再次登录时,就会直接进入到系统内部,不需要再次输入用户名和密码,就可以直接登录。这是因为我们的浏览器存储了该网站的cookie,当我们向网站发送请求时,网站检测到了cookie,识别出我们的身份,于是就不需要我们再次输入用户名和密码认证了。除此之外,我们在登录网站后,进行一些操作的时候,也会在请求数据包中携带cookie信息,这样可以帮助网站识别这些操作究竟是哪个用户的操作。使用cookie(或者session)识别用户身份是当前网站使用的主流手段。目前网站使用的HTTP协议,是一种无状态的协议,简单来说,就是客户端发送请求,服务器根据请求处理后给予应答。因此,服务器很难判断这些请求究竟是哪个用户发送过来的,通过IP地址等方式无法真正的区分不同的用户,因此我们必须使用cookie(或者session)。
事实上,cookie由服务器产生,发送给客户端,并且要求客户端保留cookie,并且在数据包发送时携带。如下所示:
上图中的set-cookie属性,就是服务器给客户端发送的cookie。
当然你也可以通过一下路径查看cookie
Cookie的表示
一般情况下,cookie是以键值对进行表示的(key-value),例如name=jack,这个就表示cookie的名字是name,cookie携带的值是jack。
Cookie的创建与发送
@RestController
public class servlet05 extends HttpServlet {
@GetMapping("/cook01")
protected void service(HttpServletRequest rep, HttpServletResponse resp) throws IOException {
//Cookie的创建
Cookie cookie = new Cookie("name","admin");
//发送和响应Cookie对象
resp.addCookie(cookie);
}
}
Cookie的获取
@RestController
public class servlet06 extends HttpServlet {
@GetMapping("/cook02")
protected void service(HttpServletRequest rep, HttpServletResponse resp) throws IOException {
//获取Cookie数组
Cookie[] cookies = rep.getCookies();
//判断Cookie是否为空
if(cookies !=null&&cookies.length>0){
//遍历
for(Cookie cookie:cookies){
//获取名称和值
String name = cookie.getName();
String value = cookie.getValue();
System.out.println(name);
System.out.println(value);
}
}
}
}
注意:因为我没有本地自己装tomcat,所以我就换了种方法实现
Cookie设置到期时间
介绍
除了Cookie的名称和内容外,我们还需要关心一个信息,到期时间,到期时间用来指定该cookie 何时失效。默认为当前浏览器关闭即失效。我们可以手动设定cookie的有效时间(通过到期时间计算),通过setMaxAge(int time);方法设定 cookie 的最大有效时间,以秒为单位。
到期时间的取值
负整数
若为负数,表示不存储该 cookie。cookie的 maxAge 属性的默认值就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么 cookie 就会消失。
正整数
若大于0的整数,表示存储的秒数。表示cookie对象可存活指定的秒数。当生命大于O时,浏览器会把Cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie也会存活相应的时间。
零
若为0,表示删除该 cookie。cookie 生命等于0是一个特殊的值,它表示cookie被作废!也就是说,如果原来浏览器已经保存了这个Cookie,那么可以通过Cookie的 setMaxAge(O)来删除这个Cookie。无论是在浏览器内存中,还是在客户端硬盘上都会删除这个Cookie。
实战
@RestController
public class servlet07 extends HttpServlet {
@GetMapping("/cook03")
protected void service(HttpServletRequest rep, HttpServletResponse resp) throws IOException {
//Cookie的创建
Cookie cookie = new Cookie("name1","admin1");
//Cookie到期时间设置
cookie.setMaxAge(-1);//关闭浏览器失效
//发送和响应Cookie对象
resp.addCookie(cookie);
//Cookie的创建
Cookie cookie2 = new Cookie("name2","admin2");
//Cookie到期时间设置
cookie2.setMaxAge(30);//存活30秒
//发送和响应Cookie对象
resp.addCookie(cookie2);
//Cookie的创建
Cookie cookie3 = new Cookie("name3","admin3");
//Cookie到期时间设置
cookie3.setMaxAge(0);//删除cookie3
//发送和响应Cookie对象
resp.addCookie(cookie3);
}
}
分析:
你发现只有两个,原因就是当到期时间设置为0时,就是删除了这个cookie
当我们30s后重新去访问(Cookie的获取哪里写的路径:目的时后台打印)时会发现,只剩下
当我们关闭浏览器后,再次去访问(Cookie的获取哪里写的路径:目的时后台打印)时会发现,全没了
注意:之前在Cookie的创建与发送标题中的Cookie这里已经被我提前删除了,所以并没有它。
Cookie的注意点
1、Cookie保存在当前浏览器中。在一般的站点中常常有记住用户名这样一个操作,该操作只是将信息保存在本机上,换电脑以后这些信息就无效了。而且cookie 还不能跨浏览器。
2、Cookie存中文问题Cookie 中不能出现中文,如果有中文则通过URLEncoder.encode()来进行编码,获取时通过URLDecoder.decode()来进行解码。
String name = “姓名"
String value = "张三";
// 通过 URLEncoder.encode()来进行编码
name = URLEncoder.encode(name);
value = URLEncoder.encode(value) ;
// 创建cookie对象
Cookie cookie = new Cookie(name,value) ;
//发送cookie对象
response.addcookie(cookie);
//获取时通过uRLDecoder.decode()来进行解码
URLDecoder.decode(cookie.getName()) ;
URLDecoder.decode(cookie.getValue()) ;
3、同名Cookie问题如果服务器端发送重复的Cookie那么会覆盖原有的Cookie。
4、浏览器存放Cookie的数量不同的浏览器对Cookie也有限定,Cookie的存储有是上限的。Cookie是存储在客户端(浏览器)的,而且一般是由服务器端创建和设定。后期结合Session来实现回话跟踪。
Cookie的路径
介绍:
Cookie的setPath设置cookie的路径,这个路径直接决定服务器的请求是否会从浏览器中加载某些cookie。
情景一:当前服务器下任何项目的任意资源都可获取Cookie对象
/*当前项目路径为:s01*/
Cookie cookie = new Cookie("xxx","xxx");
//设置路径为"/",表示在当前服务器下任何项目都可访问到cookie对象
cookie.setPath("/");
response.addcookie(cookie) ;
情景二:当前项目下的资源可获取Cookie对象(默认不设置Cookie的path)
/*当前项目路径为:s01*/
Cookie cookie = new Cookie("xxx","xxx") ;
//设置路径为"/s01",表示在当前项目下任何项目都可访问到cookie对象
cookie.setPath("/s01");//默认情况,可不设置path的值
response.addcookie(cookie) ;
情景三:指定项目下的资源可获取Cookie对象
/*当前项目路径为:s01*/
Cookie cookie = new Cookie("xxx","xxx");
//设置路径为"/s02",表示在s02项目下才可访问到cookie对象
cookie.setPath("/s02");//只能在s02项目下获取cookie,就算cookie是s01产生的,s01也不能获取它
response.addcookie(cookie) ;
情景四:指定目录下的资源可获取Cookie对象
/*当前项目路径为:s01*/
Cookie cookie = new Cookie("xxx","xxx");
//设置路径为"/s01/cook",表示在s02/cook目录下才可访问到cookie对象
cookie.setPath("/s01/cook") ;
response.addcookie(cookie) ;
总结:
如果我们设置path,如果当前访问的路径包含了cookie的路径(当前访问路径在cookie路径基础上要比cookie的范围小)cookie就会加载到request对象之中。cookie的路径指的是可以访问该cookie的顶层目录,该路径的子路径也可以访问该cookie。总结:当访问的路径包含了cookie的路径时,则该请求将带上该cookie;如果访问路径不包含cookie路径,
HttpSession
介绍
Session技术是将数据存储在服务器端的技术,会为每个客户端都创建一块内存空间 存储客户的数据,但客户端需要每次都携带一个标识ID去服务器中寻找属于自己的内 存空间。所以说Session的实现是基于Cookie,Session需要借助于Cookie存储客 户的唯一性标识JSESSIONID;
使用场景
我们先来想一个问题,这个问题就是我们在游览购物网站时,我们并没有登录,但是我们任然可以将商品加入购物车,并且进行查看,当我们退出游览器后再打开游览器进行查看时,购物车中依然有我们选择的商品,这该怎么实现呢?
当然,我们可以使用cookie,但是cookie能存放大量数据吗?这时,我们就需要一种新的技术,Session。session是存储于服务器端的特殊对象,服务器会为每一个游览器(客户端)创建一个唯一的session。这个session是服务器端共享,每个游览器(客户端)独享的。我们可以在session存储数据,实现数据共享。
原理示意图
简单使用
@RestController
public class servlet08 extends HttpServlet {
@GetMapping("/session01")
protected void service(HttpServletRequest rep, HttpServletResponse resp) throws IOException {
//获取session对象
HttpSession session = rep.getSession();
//获取session的会话标识符
String id =session.getId();
System.out.println(id);
//获取session创建时间
long creationTime = session.getCreationTime();
System.out.println(creationTime);
//获取最后一次访问时间
long lastAccessedTime = session.getLastAccessedTime();
System.out.println(lastAccessedTime);
// 判断是否是新的session对象
boolean aNew = session.isNew();
System.out.println(aNew);
}
}
标识符JSESSIONID
Session既然是为了标识一次会话,那么此次会话就应该有一个唯一的标志,这个标志就是sessionld。
每当一次请求到达服务器,如果开启了会话(访问了session),服务器第一步会查看是否从客户端回传一个
名为JSESSIONID的cookie,如果没有则认为这是一次新的会话,会创建一个新的 session对象,并用唯一的
seSSionld为此次会话做一个标志。如果有JESSIONID这个cookie回传,服务器则会根据JSESSIONID 这个值去查
看是否含有id为JSESSION值的session对象,如果没有则认为是一个新的会话,重新创建一个新的session对象,
并标志此次会话;如果找到了相应的 session对象,则认为是之前标志过的一次会话,返回该session对象,数
据达到共享。
这里提到一个叫做JSESSIONID的cookie,这是一个比较特殊的cookie,当用户请求服务器时,如果访问了
session,则服务器会创建一个名为JSESSIONID,值为获取到的 session(无论是获取到的还是新创建的)的
sessionld 的 cookie 对象,并添加到 response 对象中,响应给客户端,有效时间为关闭浏览器。
所以 Session的底层依赖Cookie来实现。
Session域对象
@RestController
public class servlet09 extends HttpServlet {
@GetMapping("/session02")
protected void service(HttpServletRequest rep, HttpServletResponse resp) throws IOException {
//获取session对象
HttpSession session = rep.getSession();
//获取域对象
session.setAttribute("uname","upwd");
session.setAttribute("uname1","upwd1");
//移除域对象
session.removeAttribute("uname");
}
}
Session对象的销毁
大纲
- 默认时间到期
- 自己设定到期时间
- 立刻失效
- 关闭浏览器
- 关闭服务器
默认时间到期
当客户端第一次请求 servlet 并且操作 session 时,session 对象生成,以 Tomcat 为例,Tomcat 中 session 默认的存活时间为 30min,即你不操作界面的时间,一旦有操作,session 会重新计时。那么 session 的默认时间可以改么?答案是肯定的。可以在 Tomcat 中的 web.xml 文件中进行修改。如下图:
自己设定到期时间
当然除了以上的修改方式外,我们也可以在程序中自己设定 session 的生命周期,通过 session.setMaxInactiveInterval(int); 来设定 session 的最大不活动时间,单位为秒。
HttpSession session = req.getSession();
session.setMaxInactiveInterval(5);
当然我们也可以通过 getMaxInactiveInterval(); 方法来查看当前 Session 对象的最大不活动时间。
立刻失效
或者我们也可以通过 session.invalidate(); 方法让 session 立刻失效。
session.invalidate();
关闭浏览器
session 的底层依赖 cookie 实现,因为不同用户访问服务器要判别到底是使用哪个 session,所以当用户第一次访问服务器的时候往往会把一个 session id 通过 cookie 存储到用户端,并且该 cookie 的有效时间为关闭浏览器,从而 session 在浏览器关闭时也相当于失效了(因为没有 session id 再与之对应)。如下图,关闭后再打开,重新给浏览器分配了个 session id。
需要注意的是这里只是 cookie 失效了,你再访问相当于服务器把你当成了新用户,又给你创建了一个 session,并没有把之前的 session 对象销毁。
关闭服务器
当非正常关闭服务器时,session 销毁;当正常关闭服务器时,session 将被序列化到磁盘上,在工作空间 work 目录下的 SESSION.ser 文件中,如果对象被保存在了 session 中,服务器在关闭时要把对象序列化到硬盘,这个对象就必须实现 Serializable 接口,下次启动服务时,自动加载到内存。如下图,正常关闭后可以看到文件夹中多了一个 SESSIONS.ser 文件,再次启动服务器则文件消失。
ServletContext
介绍:
ServletContext是Servlet规范中的一个对象,它代表了当前Web应用程序的上下文(Context)。这个上下文包括了整个Web应用程序的信息,可以被Web应用中的所有Servlet共享。可以将ServletContext看作是一个全局存储区,用于存储和访问Web应用中的全局数据和资源。
我们将ServletContext和Cookie、Session做一个简单对比,如下图:
我们可以把ServletContext当成一个公用的空间,可以被所有的客户访问,如上图,A、B、C三个客户端都可以访问。WEB容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext,它代表当前Web应用,并且它被所有客户端共享。
由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。公共聊天室就会用到它。
当web应用关闭、Tomcat关闭或者Web应用reload的时候,ServletContext对象会被销毁。
获取ServletContext
常用方法
获取当前服务器信息:
获取项目的真实路径:
效果:
三大域对象
三大域对象的数据作用范围图解 :
- 请求域
客户端发送一次的请求,以及,请求转发
- 会话域 :
同一个客户端,跨多个请求 传递信息,举例:servletA可以servletB、C、D也可以接受信息,只要拿到session对应的cookie
- 应用域
跨客户端 , 另一个客户端也能取到数据
- 所有域 合体
结言:
你好,我是Blue. 为帮助别人少走弯路而写博客 !!!
如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 😄 (^ ~ ^) 。想看更多 那就点个关注吧 我会尽力带来有趣的内容 😎。
如果你遇到了问题,自己没法解决,可以私信问我。
感谢订阅专栏 三连文章!!