今日内容
- 会话技术。会话技术包括两个:
- Cookie
- Session
- JSP的入门学习:JSP也是一种服务器的动态资源,servlet也是一种服务器的动态资源。
会话技术
-
会话:一次会话中包含多次请求和响应。一次会话就像是人与人的一次见面聊天,说第一句话的时候就是一次聊天开始了,你一次见面聊天两个人之间是多次问与答的过程,然后你聊天中有一个人离开了,这次聊天就结束了。会话也是一样的,浏览器发送第一句请求,会话就开始,浏览器和服务器其中有一个人走了会话就结束了,而且一次会话你两个人之间可以进行多次请求和响应的交流。可以看下面这个图来体会。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mLuo08io-1670486882104)(Cookie&Session&JSP入门/image-20221201100542256.png)]
-
一次会话:浏览器第一次给服务器资源发送请求,会话建立(即会话开始),直到有一方断开为止(会话结束)。断开是指,你浏览器关闭了(比如你点击浏览器的右上角的叉、或者你任务管理器把浏览器进程关闭了)或者你是服务器关闭或重启了。
-
会话的出现有什么好处:会话技术可以让浏览器和服务器在一次会话的范围内的多次请求间可以共享数据。
-
一个使用会话的例子:你逛京东,你想买一台电脑,你就点击那台电脑的加入购物车按钮,把它加入购物车,这就是一次请求了,然后它给你一个响应。然后你看到一个洗发水,你也想买,你也点击加入购物车,这又是一次请求了,然后也给你一个响应。然后你点击去结算,这也是一次请求,且这次请求知道你前几次请求所挑选的物品是什么,这里就是因为用到了会话技术,所以你点击去结算按钮的这次请求就能知道其他请求响应中的数据(你看你点击了去结算,它就知道你购物车里面选了什么,然后给你计算价格)。可能会你会想这些处理不能浏览器来处理后,然后再把购物车里面的数据都是提交给服务器吗?答:不行,因为这些处理逻辑不是浏览器能处理的,这些逻辑处理都是服务器做的,所以这里判断哪些商品加入购物车的这个动作是服务器做的(浏览器能做的逻辑处理就只能是那种的表单校验、js语句做动态效果的等一些简单的逻辑处理,像下面这种处理一般都是放在服务器那边做的)。总之,会话技术让你浏览器的某次请求能去看到前几次请求响应的数据的,比如你第一次登入一个网站,它给你提示,欢迎你第一次使用。然后你后面几次登入,就给你提示欢迎回来。即你后面几次登入就知道了前面已经登入过了,这也是用了会话技术。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1a80kuyA-1670486882105)(Cookie&Session&JSP入门/image-20221130153624807.png)]
-
会话技术有两种,一个是客户端会话技术,一个是服务器端会话技术。客户端会话技术会把会话几次请求响应的数据存到客户端,服务器端会话技术会把几次请求响应的数据存到服务器端。
在javaWeb中客户端会话技术用的是:Cookie。其他语言的客户端会话技术可能就不是用Cookie了。
在javaWeb中服务器端的会话技术用的是:Session。其他语言的服务器会话技术可能就不是用Session了。
Cookie
-
Cookie的概念:这是一种客户端会话技术,这个技术把会话几次请求响应的数据存到客户端。
-
Cookie客户端会话技术的模型
一个浏览器和服务器之间的多次请求响应是一个会话,不同浏览器和一个服务器的多次请求和响应是多次会话。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WVXXde2Y-1670486882106)(Cookie&Session&JSP入门/image-20221201103845887.png)]
每一个用户的浏览器和一个服务器之间的多次请求和响应是一次会话。比如下图这3个浏览器是不同电脑的3个谷歌浏览器,他们和一个服务器之间的多次请求和响应就是3个会话。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6QpfQ7On-1670486882107)(Cookie&Session&JSP入门/image-20221201103027043.png)]
每一个浏览器会有自己一个存Cookie数据的地方,且一个浏览器里面的一个网站可以存多个Cookie键值对,即你Cookie的键值对是依赖于浏览器里的网站的(注意:这里看到的Cookie数据都是持久化的Cookie,没有持久化的Cookie就是浏览器关闭Cookie就消失了,即没有持久化的Cookie数据是保存到浏览器内存里面的,只要浏览器这个进程关闭了那个没有持久化的Cookie就会消失。下面这个截图显示的各网站保存的Cookie数据,包括已经持久化的和在内存里的cookie数据,但是在内存里的cookie数据在浏览器关闭的时候自动就消亡了)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HbJnraYT-1670486882107)(Cookie&Session&JSP入门/image-20221201104352570.png)]
Cookie原理的模型如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zh2xANBP-1670486882108)(Cookie&Session&JSP入门/无标题.png)]
注意:你让一个网站保存两个键一样的Cookie,那么后面保存的Cookie会把前面保存的Cookie的键的值给覆盖了。但是要是你浏览器原来有的Cookie的键和你后面保存的Cookie的键不一样,那么这个浏览器的这个网站会保存两个Cookie对象,不会产生覆盖作用。
-
快速入门。
Cookie技术的使用步骤:
-
浏览器第一次向服务器发送请求
-
服务器创建Cookie对象(指创建一个类的对象),并绑定数据
使用new Cookie(String name, String value)这个构造方法来创建Cookie对象
-
服务器通过响应对象发送Cookie对象
通过HttpServletResponse接口的void addCookie(Cookie cookie) 方法来让响应对象携带Cookie对象发送到浏览器中去。
-
浏览器拿到这个响应信息,会把响应信息里面的Cookie值保存到浏览器的这个网站里面
-
浏览器再一次发送请求,会把浏览器中对应网站的Cookie键值对数组数据发送给服务器。比如你要是访问CSDN的网站的某个资源,那么你发送请求就会把你浏览器里面CSDN网站保存的Cookie键值对数组数据都发送到那个CSDN的服务器的这个资源里面去。
-
服务器拿到Request对象携带着的Cookie对象数组,然后使用这些Cookie对象。你服务器本次处理也可以向浏览器发送一个Cookie对象(要是你这里发送的新的Cookie对象的键和浏览器里面的这个网站的Cookie的键一样,浏览器拿到这个后面发送的Cookie对象,将会覆盖到原来的Cookie键值对。要是这里发送的新的Cookie对象和原来浏览器这个网站存的Cookie键值对的键不一样,浏览器就会把这个新的Cookie键值对保存下来,这样浏览器保存这个网站的Cookie键值对就多了一个)。
通过HttpServletRequest接口的Cookie[] getCookies() 方法来获取Cookie对象数组。因为浏览器那里会存储多个Cookie,它会一起发过来,所以这里是返回Cookie数组。
-
然后下一次浏览器发送请求的时候,还是会把它那个网站保存的所有Cookie对象都发送到服务器的。
-
然后服务器处理后也可以发送一个新Cookie对象给浏览器(不发送一个新的Cookie对象也没有关系)……
-
-
例子:
package cn.cookie; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * * Cookie快速入门 * */ @WebServlet("/cookieDemo2") public class CookieDemo2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //3.获取Cookie对象数组 Cookie[] cs = request.getCookies(); //获取数据,遍历Cookies拿到浏览器传过来的每个Cookie对象的键值对 if(cs != null){ for (Cookie c : cs) { String name = c.getName();//拿到键 String value = c.getValue();//拿到值 System.out.println(name+":"+value); } } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
package cn.cookie; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * * Cookie快速入门 * */ @WebServlet("/cookieDemo1") public class CookieDemo1 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.创建Cookie对象 Cookie c = new Cookie("msg","hello");//一个Cookie对象里面就存一个键值对。创建对象的时候我们可以指定这个Cookie对象里面键值对的值。 //2.服务器这边用response对象发送Cookie对象 response.addCookie(c); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
我们先访问/cookieDemo1然后再访问/cookieDemo2。
抓包看到访问/cookieDemo1的时候。看到默认浏览器这边有两个Cookie键值对数据,然后发送到服务器了,现在我们不去了解这两个Cookie键值对。我们还看到响应头里面发送了我们设置的Cookie对象数据到浏览器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qK4HI5sx-1670486882110)(Cookie&Session&JSP入门/image-20221201144105296.png)]
抓包看到访问/cookieDemo2的时候,请求头发送了3个Cookie键值对。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gqe6LMC9-1670486882112)(Cookie&Session&JSP入门/image-20221201144402972.png)]
idea打印结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H3VYr8Fx-1670486882115)(Cookie&Session&JSP入门/image-20221201143307316.png)]
然后我们用另一个浏览器来访问这个/cookieDemo2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WjmieXdB-1670486882118)(Cookie&Session&JSP入门/image-20221201144553070.png)]
没有在idea中打印msg这个Cookie数据。这说明了Cookie数据是依赖于某个浏览器的。你在这个浏览器里面有存这个Cookie数据,在另一个浏览器中看不到这个Cookie数据。
-
Cookie的实现原理是这样的:浏览器发送请求,然后服务器设置Cookie对象,然后把cookie键值对数据写到这个Cookie对象里面,然后给Response对象,然后这个Tomcat会给你把Response里面的数据都转为变为http1.1协议的格式发送到浏览器里面,这个你设置的cookie键值对数据就是保存在http数据的set-cookie这个头里面给浏览器。然后浏览器发起请求的时候,也是一样,把浏览器里面保存的cookie键值对数据装到http数据的cookie这个请求头里面,发送给服务器,然后Tomcat会把http的数据变为Request对象在服务器里面使用。所以Cookie的原理是:基于http的响应头set-cookie和请求头cookie来实现的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lBQxcrsf-1670486882119)(Cookie&Session&JSP入门/image-20221201145653204.png)]
-
对于Cookie你可能会存在的一些疑问:
-
一次可不可以发送多个cookie?答:可以。我们只要创建多个Cookie对象,然后使用response调用多次addCookie方法发送cookie即可。
例子:
package cn.cookie; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/cookieDemo3") public class CookieDemo3 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //可以一次响应发送多个Cookie数据,举例如下: //1.创建Cookie对象 Cookie c1 = new Cookie("msg","hello"); Cookie c2 = new Cookie("name","zhangsan"); //2.发送Cookie response.addCookie(c1); response.addCookie(c2); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
看我们访问/cookieDemo3的结果:看到他响应里面是发送了两个set-cookie这个响应头数据过来。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hTNB3Io3-1670486882121)(Cookie&Session&JSP入门/image-20221201151630489.png)]
-
cookie在浏览器中保存多长时间?
这个分为两种情况:
-
默认情况下,当浏览器关闭后,Cookie数据被销毁。即你Cookie数据保存到你浏览器的内存里面。只要你浏览器从内存里面被移除了,就那些Cookie数据就会被删除。
-
还有一种就是你设置了cookie在浏览器中持久化存储,这样的话,你关闭浏览器cookie数据也不会被删除。这些数据将会保存到那个浏览器相关联的一个文件夹里面。
持久化存储就需要用到一个Cookie对象的方法:setMaxAge(int seconds)。
例子如下:
package cn.cookie; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /* 这个setMaxAge(int seconds)方法的参数是正数、0、或者是负数都是不一样的效果。 1. 正数:会将Cookie数据写到浏览器对应的硬盘文件中去做持久化存储。且你这个正数的数值代表着cookie的存活时间为多少秒,时间到后,cookie文件自动被删除。 2. 负数:你不设置这个值,默认就是负数的。他代表不持久化存储,你浏览器从内存里被杀死,这个cookie就消失了。 3. 零:删除cookie信息。你服务器拿到这个Cookie对象,然后你setMaxAge(0),然后传到浏览器时,那个对应的cookie就会被删除了。 */ @WebServlet("/cookieDemo4") public class CookieDemo4 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.创建Cookie对象 Cookie c1 = new Cookie("msg","setMaxAge"); //2.设置cookie的存活时间 c1.setMaxAge(30);//将cookie持久化到硬盘,30秒后会自动删除cookie文件。这个就是设置这个cookie在硬盘中保存,且设置存活30秒,要是30秒过了,那个cookie文件就会被自动删除,要是还没到30秒,你又给浏览器响应告诉浏览器的那个快到时间的cookie存活时间为30秒,那么浏览器这个cookie的存活时间又会开始从30秒来倒计时。 //c1.setMaxAge(-1);//设置为负数就是cookie不持久化,没有设置这个c1.setMaxAge(-1);他默认也是负数。他表示你cookie保存在电脑内存的里的浏览器内存里,会随着浏览器在内存里被移除而移除。 //c1.setMaxAge(0);//删除Cookie,他能把浏览器对应网站里的键和这个c1一样的cookie文件给删除了。 //3.发送Cookie response.addCookie(c1); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
-
-
cookie能不能存中文?
在tomcat 8 之前 cookie对象中不能直接存储中文的键和值。tomcat 8之前我们需要将中文数据转码,一般采用URL编码来转码(即把一个字节转为%E3这种的),然后转码后保存到Cookie对象里面,然后放到Response对象里面进行传输给浏览器,然后到时候浏览器发送请求把Cookie发送到服务器,服务器拿到Cookie对象的时候再用URL解码来使用这个数据。
在tomcat 8 之后,cookie支持中文数据(支持中文的值,但是键不支持中文)。虽然tomcat 8 之后支持了中文,但是对于特殊字符(比如空格也是特殊字符)还是不支持的,特殊字符还是要使用URL编码后再存到Cookie对象里面,然后再放到Response对象里面传输。然后到时候浏览器发送请求把Cookie发送到服务器,服务器拿到Cookie对象的时候再用URL解码来使用这个数据。(这里用URL编码具体怎么做去看下面那个"Cookie小案例练习"的代码就行了)
例子:
package cn.cookie; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/cookieDemo5") public class CookieDemo5 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.创建Cookie对象 Cookie c1 = new Cookie("mmm","您好");//可以 Cookie c2 = new Cookie("消息","您好");//不可以 //2.发送Cookie response.addCookie(c1); response.addCookie(c2); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
-
cookie共享问题?
-
假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?
比如现在是这样的:一个服务器里面部署了两个项目。那么这个day15和day16这两个项目之间能不能共享呢?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gaNGOOxD-1670486882123)(Cookie&Session&JSP入门/image-20221201163922526.png)]
答:默认情况下这两个项目之间是不能共享cookie数据的。但是你可以用setPath(String path)这个方法来设置cookie的能共享的范围。你没有自己特别来用这个setPath(String path)方法设置能共享的范围,就表示设置能共享的范围是当前的虚拟目录,即你输入的网址
http://主机ip:端口/虚拟目录/
前缀和new Cookie对象的那个资源的访问网址的http://主机ip:端口/虚拟目录/
前缀一样的都可以共享那个cookie资源。即你那个服务器的某个Servlet类new了Cookie对象,然后通过Response对象把cookie资源保存到浏览器这边,然后你浏览器只要发送的请求的网址的前缀是和这个new Cookie对象的那个资源的访问地址的http://主机ip:端口/虚拟目录/
一样,那么你浏览器发送请求,就能会发送这个cookie数据到服务器。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b36ONgr3-1670486882124)(Cookie&Session&JSP入门/image-20221201165100088.png)]
但是你要是下面这样写代码的话,默认的就是只要你浏览器发送访问的是
请求的地址的前缀
和访问new那个Cookie对象的资源的地址的http://主机ip:端口/
前缀一样的请求,都会在发送请求的时候把那个cookie键值对数据发送到服务器。package cn.cookie; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/cookieDemo5") public class CookieDemo5 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.创建Cookie对象 Cookie c1 = new Cookie("msg","你好"); //默认的就相当于c1.setPath("/day16");//这个day16是访问这个资源的虚拟目录名 //2.设置path,让当前服务器下部署的所有项目共享Cookie信息 c1.setPath("/");//你这么设置就是相当于你告诉浏览器这个cookie数据,浏览器只要发送"http://主机ip:端口/……"的请求和访问本资源的"http://主机ip:端口/……"端口号之前的前缀都一样,那个请求发送的时候都把这个cookie数据发送到服务器。 //3.发送Cookie response.addCookie(c1); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
其实就相当于是这样的:你服务器new了一个Cookie对象然后给Response对象,然后把Response对象传给浏览器,然后浏览器会想你的这个Cookie数据是给什么样的请求用的,把这个Cookie数据放到对应的容器里面去,要是那个容器之前没有,浏览器会建一个容器,把cookie信息放进容器。比如你设置创建Cookie的时候没有设置c1.setPath(“/”);就会默认把这个Cookie数据放到一个名字是
http://主机ip:端口/虚拟目录/
的容器里面。(注意这个名字是new Cookie对象的那个资源的访问地址的前缀),然后你只要输入请求的地址前缀和这个http://主机ip:端口/虚拟目录/
一样,就会发送这个容器里面的cookie数据到服务器。然后你要是new的Cookie对象设置了c1.setPath(“/”);然后Response对象发送响应的时候把Cookie数据给浏览器,浏览器就会把这个数据放在一个名字叫http://主机ip:端口/
的容器里面,然后你只要输入请求的地址是http://主机ip:端口/
前缀的,就会把这个容器里面的所有cookie数据也发送给服务器。 -
不同的tomcat服务器间cookie能不能共享?
比如贴吧和百度新闻,他们其实是不同服务器下的两个项目,但是他们需要共享cookie,比如他们他们都需要知道那个用户登入的,你就可以让他们共享一个cookie资源,这样他们就可以知道是一个人登入的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tCXJLNCa-1670486882125)(Cookie&Session&JSP入门/image-20221201191128534.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nHFO3DCJ-1670486882127)(Cookie&Session&JSP入门/image-20221201191213897.png)]
这种不同服务器之间共享cookie的情况,你可以用这个setDomain(域名字符串)方法来做,这个方法是:设置了Cookie对象用这个方法,可以做到一级域名相同的网址,都可以共享这个这个Cookie资源,即,发送请求的时候会把这个Cookie发送到服务器。相当于也是建了一个容器,这个容器叫“你设置的域名字符串”。然后只要你访问的资源的网址的一级域名和这个域名字符串一样,就会发送请求的时候把这个Cookie数据一起发送的服务器。比如你服务器里new某个Cookie对象时,设置setDomain(“.baidu.com”),那么https://tieba.baidu.com/index.html和https://news.baidu.com/两个网址都可以做到访问的时候,发送浏览器中的这个设置了setDomain(“.baidu.com”)的cookie数据。
-
-
-
Cookie的特点和作用
特点:
- cookie存储数据在客户端浏览器。你把Cookie是把数据存在浏览器,但是Session是把数据存到服务器,把数据存到服务器数据的安全性会更高。
- 浏览器对于单个cookie 的大小有限制(一般是4kb,不同浏览器限制不同)且浏览器对同一个域名下的总cookie数量也有限制(一般是20个,不同浏览器限制不同)
作用:
-
cookie一般用于存出少量的不太敏感的数据
-
cookie一般用于:在不登录的情况下(这里为什么说是不登录的情况下呢?因为登录情况下你客户端浏览器要拿到你保存的设置就很简单,直接让服务器去数据库里面拿设置信息就行了。但是不登录情况下,你要服务器给你浏览器发送一个你本地设置好的样式的界面,就得依靠浏览器本地的cookie了),也能完成服务器对客户端的身份识别,然后给你响应对应的数据。比如:你设置了百度的页面的样式,你设置样式也是会发送请求到服务器的,然后服务器会给你创造一个Cookie数据给你的浏览器,浏览器保存了,然后你每次进到百度页面的时候,会把这个cookie信息发送给服务器,服务器都给你对应样式的html的布局。总之cookie能达到很多目的,但是这些目的都是让浏览器能记住你。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxxIorz8-1670486882128)(Cookie&Session&JSP入门/image-20221201193306671.png)]
Cookie小案例练习
案例:记住上一次访问时间。
-
需求:
- 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问
- 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:2018年6月10日11:50:20
-
分析图示:这里可以看到这个案例里面没有登入,但是服务器也能识别你的身份信息,知道你是不是第一次登入,知道你上一次访问的时间。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YTnnySF0-1670486882129)(Cookie&Session&JSP入门/image-20221201195435036.png)]
-
代码:
package cn.cookie; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLDecoder; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Date; /** 在服务器中的Servlet判断是否有一个名为lastTime的cookie 1. 有:不是第一次访问 1. 响应数据:欢迎回来,您上次访问时间为:2018年6月10日11:50:20 2. 写回Cookie:lastTime=2018年6月10日11:50:01 2. 没有:是第一次访问 1. 响应数据:您好,欢迎您首次访问 2. 写回Cookie:lastTime=2018年6月10日11:50:01 */ @WebServlet("/cookieTest") public class CookieTest extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置响应的消息体的数据格式以及编码 response.setContentType("text/html;charset=utf-8");//输出到网页里面的中文内容要是想不出现乱码(为什么英文的数据不用做这些编码的操作呢?因为英文的很多编码兼容了,但是中文没有被兼容,所以我们需要在意这些变为问题),就需要设置流的编码和浏览器对内容的解码一致就行了。这里这句语句是让浏览器用utf-8来解码,且让服务器的流用utf-8的格式来编码并写数据。Cookie对象要能存中文的数据,就要用URL编码把中文转为对应URL数据,然后传输给浏览器。到时候服务器要用Cookie数据的时候,把Cookie数据用URL来解码就行了。然后下载提示框里面的显示中文文件名的问题,我们就需要用工具类,根据不同浏览器来把文件名编码为不同格式的数据,因为你不能决定浏览器下载提示框解码的规则,你只能把你要显示的中文编译为它能识别的编码格式。对于网页内容,我们是因为我们可以决定浏览器用什么格式来解码,所以我们才那样设置。对于下载提示框,是因为我们决定不了它用什么编码,所以我们只能去适应它。对于Cookie传输,因为Cookie传输的时候不能传特殊字符,tomcat8之前不能传输中文,所以我们需要把数据编码后传输,到时候用的时候自己来解码,放在用也是自己来用自己来解码的,为什么编码为URL呢,因为我们比较习惯用URL来在网页中传输。 //1.获取所有Cookie Cookie[] cookies = request.getCookies(); boolean flag = false;//没有lastTime,说明是第一次登入,要是你遍历的时候遍历到有Cookie的键是lastTime就把flag变为true //2.遍历cookie数组 if(cookies != null && cookies.length > 0){ for (Cookie cookie : cookies) { //3.获取cookie的名称 String name = cookie.getName(); //4.判断名称是否是:lastTime if("lastTime".equals(name)){ //有该Cookie,不是第一次访问 flag = true;//有lastTime的cookie //设置Cookie的value //获取当前时间的字符串,重新设置Cookie的值,重新发送cookie Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");//因为这里虽然中文tomcat8后能放到Cookie里面当值了,但是特殊字符仍然不能放在Cookie中做传输。所以我们需要把要放在Cookie里面的信息编码,然后再放到Response中传输。 String str_date = sdf.format(date); System.out.println("编码前:"+str_date); //URL编码。即转为%EA这种一个%两个十六进制数表示一个字节数据的格式。 str_date = URLEncoder.encode(str_date,"utf-8");//这个语句是把使用utf-8的机制(即先把汉字转为UTF-8的编码,一个汉字是3个字节,然后把字节变为URL编码)把这个字符变为URL字符。 System.out.println("编码后:"+str_date); cookie.setValue(str_date);//把Cookie数据放到你拿到的Cookie对象里面,然后传输给浏览器。注意:这里你自己new一个新的Cookie,把数据存到Response里面进去,然后传给浏览器,去覆盖浏览器的那个lastTime键的Cookie也行。 //Cookie cookie1=new Cookie("lastTime",null);//你自己new一个新的Cookie也行 //设置cookie的存活时间 cookie.setMaxAge(60 * 60 * 24 * 30);//这里设置存活的时间为一个月 response.addCookie(cookie); //响应数据 //获取Cookie的value。 String value = cookie.getValue(); System.out.println("解码前:"+value); //把这个value解码,因为前面我们为了能放在Cookie对象里面,我们把字符串进行了编码。 value = URLDecoder.decode(value,"utf-8");//把URL编码转为字节然后字节按UTF-8变为汉字 System.out.println("解码后:"+value); response.getWriter().write("<h1>欢迎回来,您上次访问时间为:"+value+"</h1>");//因为我们前面说了response.setContentType("text/html;charset=utf-8");告诉浏览器这个写的文档是html的,所以可以用这个html标签。 //你不写标签也行。也可以解析。 response.getWriter().write("欢迎回来,您上次访问时间为:"+value); break; } } } if(cookies == null || cookies.length == 0 || flag == false){ //没有,第一次访问 //设置Cookie的value //获取当前时间的字符串,重新设置Cookie的值,重新发送cookie Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String str_date = sdf.format(date); System.out.println("编码前:"+str_date); //URL编码 str_date = URLEncoder.encode(str_date,"utf-8"); System.out.println("编码后:"+str_date); Cookie cookie = new Cookie("lastTime",str_date); //设置cookie的存活时间 cookie.setMaxAge(60 * 60 * 24 * 30);//一个月 response.addCookie(cookie); response.getWriter().write("<h1>您好,欢迎您首次访问</h1>"); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
效果:
第一次访问:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tYzmxak0-1670486882130)(Cookie&Session&JSP入门/image-20221201211452080.png)]
第二次访问:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QRlOnmnJ-1670486882131)(Cookie&Session&JSP入门/image-20221201211459793.png)]
JSP:入门学习
-
概念:JSP,即Java Server Pages。java服务器端页面。JSP的文件可以理解为:是一个特殊的页面,其中既可以写html标签,又可以写java代码。
比如:下面这个jsp资源在浏览器中被访问后会做什么。(我们可以看到这个jsp文件里面可以写java代码也可以写html的标签)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-67Hs4wRd-1670486882132)(Cookie&Session&JSP入门/image-20221202091151593.png)]
在浏览器访问后,会在浏览器中输出了jsp文件夹中的html的内容。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8kQwffCZ-1670486882133)(Cookie&Session&JSP入门/image-20221202090838102.png)]
又在idea控制台打印了java的输出。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sDJSr043-1670486882134)(Cookie&Session&JSP入门/image-20221202090924920.png)]
-
JSP的作用是:简化开发。比如你要在一个页面上既要有html等静态资源的内容,又有靠代码生成的动态动态内容。比如下面这样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6cC5HzHT-1670486882134)(Cookie&Session&JSP入门/image-20221202092027475.png)]
那么你只能在Servlet里面写动态生成的代码,然后自己一行行地写页面要用到的html标签。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DnlOgETS-1670486882135)(Cookie&Session&JSP入门/image-20221202092352894.png)]
这样就很麻烦。但是你用jsp的话,你就像是在写一个类似html的文件,然后需要动态获取的内容,html标签对应的位置直接写一个java代码就行了。就是jsp中可以在一个页面里既写html标签,又写java代码。
-
JSP文件实现既能写java代码又能写html标签的原理:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9fwBtHxr-1670486882136)(Cookie&Session&JSP入门/image-20221202094107936.png)]
你服务器拿到浏览器发起的请求,然后服务器会解析这个请求,找这个地址的资源,要是没有找到这个资源就给返回404。要是找到了这个jsp资源,服务器会把这个JSP资源转为java文件(不一定叫_index.java哈,这里的这个解析jsp文件生成的java文件的名字是乱取,比如就叫 _index.java。)。但是浏览器访问资源也不能是.java文件,所以它会把这个java文件编译为class字节码文件。然后字节码文件是可以被浏览器访问的(你看我们写的Servlet的java类,其实浏览器来访问的也不是这个java文件,是访问的web项目中那个classes目录下的.class文件的)。而且,浏览器能访问的java类资源只有Servlet的.class资源,所以这里的 _index.class文件其实是一个Servlet类。
现在我们去查看一下这个生成的.class类长什么样。
我们先找到这个web项目目录的位置。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XBMklq77-1670486882137)(Cookie&Session&JSP入门/image-20221202095548354.png)]
然后里面有一个Work目录,它是Work目录放的是我们web项目运行时产生的资源文件。只要我们浏览器访问了那个jsp资源,这个work目录下就会生成这个jsp的运行时的文件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fGwWamfb-1670486882137)(Cookie&Session&JSP入门/image-20221202095618754.png)]
然后我们打开work目录下的work\Catalina\localhost\day16\org\apache\jsp。就看到了下面这两个文件,一个是index_jsp.java文件,一个是index_jsp.class文件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NwAo7QpL-1670486882138)(Cookie&Session&JSP入门/image-20221202095938421.png)]
打开这个java文件如下,这个java是我们程序运行时自动生成的文件。可以看到这个java文件代表的java类是继承自HttpJspBase的。我们去找到Tomcat的源码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DuKx4yGI-1670486882139)(Cookie&Session&JSP入门/image-20221202100227250.png)]
可以看到源码里这个HttpJspBase类就是继承自HttpServlet的,所以index_jsp.java类其实就是一个Servlet类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uffDNW9R-1670486882140)(Cookie&Session&JSP入门/image-20221202100516096.png)]
然后我们回去看这个index_jsp.java,里面有一个_jspService()的方法,这个方法就是类似我们Servlet的Service()方法,看到这个方法里面都是write(html标签),所以可以知道这个这个jsp其实也就是相当于你写了Servlet类,然后自己在这个类里面写动态生成的内容,也自己写html标签。只是这里是它自动给你写了而已,给程序员省事情了而已。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dkaeGppb-1670486882141)(Cookie&Session&JSP入门/image-20221202100811888.png)]
-
JSP的脚本:脚本决定了你写Java代码的方式。一共有三种脚本:
- <% 代码 %>:这里写的代码到时候会在生成这个jsp对应的java文件的_jspService()方法中。所以你Servlet类的service方法中可以定义什么,该脚本中就可以写什么java代码。
- <%! 代码 %>:在这里写的代码会变成在jsp生成对应的java文件的成员变量或成员方法。所以这里的代码用于定义成员变量、定义成员方法、静态代码块、静态方法等,反正就是这里写的代码会写在那个jsp生成对应的java类的成员位置。即在类下,成员方法、构造方法、静态方法同级的位置生成代码。但是这个脚本用的比较少,因为你用得不好可能会出现线程安全问题,就是多人都是去访问一个资源的成员变量,然后有人修改了成员变量的值,对其他人访问这个资源会有影响。
- <%= 代码 %>:这里写的Java代码,会输出到页面上。比如你写一个变量,就会相当于在那个jsp生成对应的java类的_jspService()方法下面会写一句out.print( “这个变量的值” );。
注意:你把java代码写在脚本里面和写在脚本外面是不一样的。如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BW0Kj7jg-1670486882141)(Cookie&Session&JSP入门/image-20221202102036776.png)]
下面我们来看看,你写的jsp代码会变为什么样的java代码。
这是你写的jsp代码:
<%-- Created by IntelliJ IDEA. User: 13780 Date: 2022/11/30 Time: 19:46 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <% System.out.println("test11111:::::hello jsp"); int test2222222 = 5;//test2 %> <%! int test33333333 = 3; %> <%= "test4444444" %> System.out.println("hello jsp"); <h1>test555555555 hi~ jsp!</h1> </body> </html>
这是你work文件夹里面看到的这个jsp生成的java文件的代码:我们可以看到在不同的脚本中写的代码最后放在jsp生成的java类中的位置不同。
/* * Generated by the Jasper component of Apache Tomcat * Version: Apache Tomcat/8.5.31 * Generated at: 2022-12-02 06:14:54 UTC * Note: The last modified time of this file was set to * the last modified time of the source file after * generation to assist with modification tracking. */ package org.apache.jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent, org.apache.jasper.runtime.JspSourceImports { int test33333333 = 3; private static final javax.servlet.jsp.JspFactory _jspxFactory = javax.servlet.jsp.JspFactory.getDefaultFactory(); private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants; private static final java.util.Set<java.lang.String> _jspx_imports_packages; private static final java.util.Set<java.lang.String> _jspx_imports_classes; static { _jspx_imports_packages = new java.util.HashSet<>(); _jspx_imports_packages.add("javax.servlet"); _jspx_imports_packages.add("javax.servlet.http"); _jspx_imports_packages.add("javax.servlet.jsp"); _jspx_imports_classes = null; } private volatile javax.el.ExpressionFactory _el_expressionfactory; private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager; public java.util.Map<java.lang.String,java.lang.Long> getDependants() { return _jspx_dependants; } public java.util.Set<java.lang.String> getPackageImports() { return _jspx_imports_packages; } public java.util.Set<java.lang.String> getClassImports() { return _jspx_imports_classes; } public javax.el.ExpressionFactory _jsp_getExpressionFactory() { if (_el_expressionfactory == null) { synchronized (this) { if (_el_expressionfactory == null) { _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory(); } } } return _el_expressionfactory; } public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() { if (_jsp_instancemanager == null) { synchronized (this) { if (_jsp_instancemanager == null) { _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig()); } } } return _jsp_instancemanager; } public void _jspInit() { } public void _jspDestroy() { } public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { final java.lang.String _jspx_method = request.getMethod(); if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) { response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD"); return; } final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; final javax.servlet.ServletContext application; final javax.servlet.ServletConfig config; javax.servlet.jsp.JspWriter out = null; final java.lang.Object page = this; javax.servlet.jsp.JspWriter _jspx_out = null; javax.servlet.jsp.PageContext _jspx_page_context = null; try { response.setContentType("text/html;charset=UTF-8"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; out.write("\n"); out.write("\n"); out.write("\n"); out.write("<html>\n"); out.write("<head>\n"); out.write(" <title>$Title$</title>\n"); out.write("</head>\n"); out.write("<body>\n"); out.write("\n"); System.out.println("test11111:::::hello jsp"); int test2222222 = 5;//test2 out.write('\n'); out.write('\n'); out.write('\n'); out.write('\n'); out.print( "test4444444" ); out.write("\n"); out.write("\n"); out.write("\n"); out.write("System.out.println(\"hello jsp\");\n"); out.write("<h1>test555555555 hi~ jsp!</h1>\n"); out.write("\n"); out.write("</body>\n"); out.write("</html>\n"); } catch (java.lang.Throwable t) { if (!(t instanceof javax.servlet.jsp.SkipPageException)){ out = _jspx_out; if (out != null && out.getBufferSize() != 0) try { if (response.isCommitted()) { out.flush(); } else { out.clearBuffer(); } } catch (java.io.IOException e) {} if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); else throw new ServletException(t); } } finally { _jspxFactory.releasePageContext(_jspx_page_context); } } }
-
JSP的内置对象
在jsp页面中不需要获取和创建,可以直接使用的对象,我们就称它为内置对象。
jsp一共有9个内置对象。今天我们先来学习3个:
-
request:这个request对象和我们Servlet中的Request对象一样,所以使用就不用多说了。我们在<% 代码 %>中的代码可以直接用这个request对象,因为我们写这个这里的代码最终会放在
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
这个方法的方法体中,因为这个方法有request对象,所以我们这个<% 代码 %>中的代码可以直接用这个request对象
-
response:这个response对象和我们Servlet中的response对象一样,所以使用就不用多说了。我们在<% 代码 %>中的代码可以直接用这个response对象,因为我们写这个这里的代码最终会放在
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
这个方法的方法体中,因为这个方法有response对象,所以我们这个<% 代码 %>中的代码可以直接用这个response对象
-
out:为什么<% 代码 %>中的代码能直接使用这个out对象呢?因为我们看到jsp文件生成的java类的_jspService()这个方法的方法体里面有:javax.servlet.jsp.JspWriter out = null;和pageContext.getOut();这两个语句,即创建了out对象。然后因为我们在<% 代码 %>中写的代码是将来是会填充到这两句语句之后的,所以我们的<% 代码 %>里面的代码可以直接用这个out对象。这个out对象其实就是一个字符输出流对象,可以将数据输出到页面上。out.write()和我们Servlet中的response.getWriter()类似。为什么说类似呢?就是他们都可以可以把方法里的内容输出到页面上。但是他们是有区别的。
response.getWriter()和out.write()的区别:在tomcat服务器真正给客户端做出响应之前,会先找response缓冲区数据,再找out缓冲区数据。所以response.getWriter()写的数据在浏览器页面显示时永远在out.write()写的数据之前
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ut6YOkxu-1670486882142)(Cookie&Session&JSP入门/image-20221202150904461.png)]
比如:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ENyaNJXL-1670486882143)(Cookie&Session&JSP入门/image-20221202151551520.png)]
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j2c3iyZS-1670486882143)(Cookie&Session&JSP入门/image-20221202151642680.png)]
-
JSP小案例
我们今天要做的案例和前面的Cookie小案例一样。但是这次我们用jsp来做。
需求:
- 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问
- 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:2018年6月10日11:50:20
<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.net.URLEncoder" %>
<%@ page import="java.net.URLDecoder" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>itcast</title>
</head>
<body>
<%
//1.获取所有Cookie
Cookie[] cookies = request.getCookies();
boolean flag = false;//没有cookie为lastTime
//2.遍历cookie数组
if(cookies != null && cookies.length > 0){
for (Cookie cookie : cookies) {
//3.获取cookie的名称
String name = cookie.getName();
//4.判断名称是否是:lastTime
if("lastTime".equals(name)){
//有该Cookie,不是第一次访问
flag = true;//有lastTime的cookie
//设置Cookie的value
//获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = sdf.format(date);
System.out.println("编码前:"+str_date);
//URL编码
str_date = URLEncoder.encode(str_date,"utf-8");
System.out.println("编码后:"+str_date);
cookie.setValue(str_date);
//设置cookie的存活时间
cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
response.addCookie(cookie);
//响应数据
//获取Cookie的value,时间
String value = cookie.getValue();
System.out.println("解码前:"+value);
//URL解码:
value = URLDecoder.decode(value,"utf-8");
System.out.println("解码后:"+value);
%>
<h1>欢迎回来,您上次访问时间为:<%=value%></h1>
<input>
<%
break;
}
}
}
if(cookies == null || cookies.length == 0 || flag == false){
//没有,第一次访问
//设置Cookie的value
//获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = sdf.format(date);
System.out.println("编码前:"+str_date);
//URL编码
str_date = URLEncoder.encode(str_date,"utf-8");
System.out.println("编码后:"+str_date);
Cookie cookie = new Cookie("lastTime",str_date);
//设置cookie的存活时间
cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
response.addCookie(cookie);
%>
<h1>您好,欢迎您首次访问</h1>
<span></span>
<%
}
%>
<input>
</body>
</html>
写JSP过程中的拿到的一些收获:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k0A8yOeZ-1670486882144)(Cookie&Session&JSP入门/image-20221202154700044.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qERNVOt5-1670486882145)(Cookie&Session&JSP入门/image-20221202155609049.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1rpwvNmE-1670486882145)(Cookie&Session&JSP入门/image-20221202155856921.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vd6ZztIi-1670486882146)(Cookie&Session&JSP入门/image-20221202160133256.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qwMSHptK-1670486882147)(Cookie&Session&JSP入门/image-20221202160918521.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EOEpOkUC-1670486882147)(Cookie&Session&JSP入门/image-20221202161429984.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MW0jOUhZ-1670486882148)(Cookie&Session&JSP入门/image-20221202162600006.png)]
前面展示的代码的执行的结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ON2CjFye-1670486882148)(Cookie&Session&JSP入门/image-20221202162640289.png)]
Session
-
Session的概念:Session是服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中(这个服务器端的对象就是HttpSession对象)。注意:这个Session也是一个域对象,即可以有多个资源共享这个对象的数据的。
-
快速入门:
-
获取HttpSession对象。比如:HttpSession session = request.getSession();
-
使用HttpSession对象:因为他也是域对象,所以他的使用也主要是用下面这三个方法。
- Object getAttribute(String name)
- void setAttribute(String name, Object value)
- void removeAttribute(String name)
-
代码例子:
package cn.session; 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("/sessionDemo1") public class SessionDemo1 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //使用session共享数据 //1.获取session HttpSession session = request.getSession(); //2.存储数据 session.setAttribute("msg","hello session"); System.out.println("session对象的地址为:"+session);//打印org.apache.catalina.session.StandardSessionFacade@11e80d1c } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
package cn.session; 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("/sessionDemo2") public class SessionDemo2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //使用session获取数据 //1.获取session HttpSession session = request.getSession(); //2.获取数据 Object msg = session.getAttribute("msg");//我们先访问了/sessionDemo1,然后再访问/sessionDemo2资源。结果看到我们能拿到这个/sessionDemo1存的msg数据。但是我们要是把浏览器关闭了,然后再访问这个/sessionDemo2资源就会发现/sessionDemo1存到session的msg数据消失了。 System.out.println(msg); System.out.println("session对象的地址为:"+session);//打印org.apache.catalina.session.StandardSessionFacade@11e80d1c,说明两个HttpSession对象是一个对象。 } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
-
-
Session的原理:Session的实现是依赖于Cookie的。怎么说呢?看下面就行了。
一开始你浏览器本地的那个对应的网站没有保存叫JSESSIONID这个键名的Cookie,然后你发送请求给服务器访问sessionDemo1,然后这个服务器的Servlet资源里面有一个获取HttpSession的对象的request.getSession()语句,然后他一看你请求里面传没有键名为JSESSIONID的cookie,那么执行request.getSession()方法获取一个Session对象的时候就会创建一个Session对象在服务器内存里保存着并且给一个id出来,你浏览器到时候响应的时候就会自动把这个id作为键名JSESSIONID的cookie的值传给浏览器(只要你获取Session对象的时候创建了这个Session对象,服务器响应的时候都会给你传这么一个cookie过去)。服务器是自动把这个cookie放在set-cookie这个响应头里面,然后发送给浏览器的(不用你手动把cookie添加到response对象里面)。浏览器拿到这个键名JSESSIONID的cookie后会把他保存到对应容器里面(一般是保存在那个访问sessionDemo1的URL地址的,名字叫
http:到虚拟路径
的容器里面。但是值得注意的是,默认情况下,浏览器拿到这个cookie数据会保存在浏览器的内存里,所以浏览器只要关闭,这个键名为JSESSIONID的Cookie就会销毁了)。然后浏览器下一次访问符合这个容器名字的请求地址时,就会把这个容器里面的cookie键值数组对放在cookie这个请求头里发送给服务器。然后服务器拿到请求,执行对应的Servlet资源的service方法。这个service方法执行到request.getSession()方法时,他会看你有没有一个键名叫JSESSIONID的cookie,要是没有就会和第一次请求一样,给你创建一个Session并保存在服务器内存,然后给一个id,到时候服务器响应的时候把这个id放在键名JSESSIONID的cookie里面自动传给浏览器。要是服务器拿到请求,执行到request.getSession()方法的时候,他发现你请求里有一个键名叫JSESSIONID的cookie,那么服务器就会用这个键名为JSESSIONID的cookie去匹配服务器里面id匹配的session对象。然后要是找到了给你获取那个对象(要是没有找到那个id对应的session对象,就会创建一个新的id的新的session对象,然后给浏览器传过去覆盖原来的cookie),你这个servlet资源就可以拿到这个服务器内存里匹配的session对象里面的数据了,然后你可以进行一些列的操作,并给浏览器作出响应,但是这次这个servlet资源执行完就不会给你浏览器传一个键名为JSESSIONID的cookie的数据了,因为你没有生成新的Session对象。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n5TWSdCr-1670486882149)(Cookie&Session&JSP入门/image-20221202172441613.png)]
-
关于Session的一些疑问:
-
当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
-
默认情况下,不是同一个。即你服务器不关闭,然后打开浏览器发送请求,访问一个有service()方法里有写request.getSession()语句的servlet资源,因为本次是刚打开浏览器且是第一次访问,所以这一次发送的请求里没有键名为JSESSIONID的cookie。所以你访问这个servlet资源后,服务器会给你生成一个session对象和id,且给你返回键名为JSESSIONID值为id的cookie,然后你浏览器会保存这个cookie。然后你浏览器不关闭下一次访问服务器会带着这个cookie去服务器,然后服务器匹配id,如果找到了id匹配的session对象,服务器会给你返回你浏览器第一次发送请求时服务器创建的那个session对象,且不会给你返回一个键名为JSESSIONID值为id的cookie,浏览器下一次访问还是会用第一次访问后响应回来的那个键名为JSESSIONID值为id的cookie。所以要是浏览器没有关闭,几次请求,获取的是session对象将会是一个session对象。
但是要是你第一次请求服务器后拿到了响应,也拿到了键名为JSESSIONID的cookie,但是后来你把浏览器关闭了,但是服务器没有关闭,你服务器里的浏览器第一次请求创建的那个session还是会保存在服务器内存的,但是,你把浏览器关闭后再重启,然后去访问一个有service()方法里有写request.getSession()语句的servlet资源时,你发送的请求里面的键名为JSESSIONID的cookie已经消失了(因为默认服务器给你返回的键名为JSESSIONID值为id的cookie是保存在浏览器内存的)。因为你发送的请求里面没有键名为JSESSIONID的cookie,所以你这次访问一个有service()方法里有写request.getSession()语句的servlet资源时,运行到这个request.getSession()语句,服务器看到你请求里面没有键名为JSESSIONID的cookie,服务器还是会创建一个新的session对象的,然后响应的时候给你传一个新的键名为JSESSIONID的cookie。所以你客户端关闭后,服务器不关闭的一个浏览器,重启前后重启后发送请求获取的session对象不是同一个。
-
如果你响应浏览器重启前请求访问的Session和浏览器重启后访问的Session对象时同一个对象,你可以在服务器创建Cookie,且这个Cookie键为JSESSIONID,你设置Cookie有一个最大的存活时间,即让cookie持久化保存在硬盘,而不是保存在内存。比如下面这样设置,你浏览器重启前后访问的session对象就是同一个了。
package cn.cookie; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; @WebServlet("/cookieDemo3") public class CookieDemo3 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); System.out.println(session); //期望客户端关闭后,session也能相同 // Cookie c = new Cookie( "JSESSIONID" , session.getId()); Cookie c = new Cookie( "JSESSIONID" , "12345646"); c.setMaxAge(60*60); //其实这样设置之后,响应的时候是会传两个cookie过去的,一个是request.getSession();创建了对象,然会会自动生成cookie传给浏览器,一个是你自己创建的这个Cookie对象c,你手动给他response.addCookie(c);塞到响应头里面,然后传给浏览器。虽然传了两个Cookie给浏览器,但是浏览器他保存了你自己设置的cookie,没有保存他自动设置的cookie。相当于先保存了自动生成的cookie,然后你的cookie覆盖了自动生成的cookie。 response.addCookie(c); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
-
-
客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
答:不是同一个(即服务器重启前,你浏览器请求服务器,用你浏览器中的键名为JSESSIONID的cookie的id去访问服务器的session对象,和你服务器重启后,你继续用这个id去访问的session对象,这两个session对象的地址值,是不同的)。但是要确保数据不丢失,tomcat会自动完成session的钝化和活化,这样你服务器重启了,虽然你重启前的那个session对象和重启后的session对象不是一个对象,但是这两个对象里面的数据是一样的。你浏览器用原来的键名为JSESSIONID的cookie的id去访问服务器的session对象,他也会给你找到那个活化后的session对象,你也可以拿到session里面保存的数据。
- session的钝化:即在服务器正常关闭之前,将session对象序列化到硬盘上的文件夹中去。(保存在work目录中,session对象将会保存为一个叫SESSIONS.ser的文件)
- session的活化:即在服务器启动后,将session文件转化为内存中的session对象,然后那个文件会被删除。
但是要注意的是:你idea中你把服务器重启了,服务器会给你把session钝化,但是活化将会失败。
你idea点击停止运行,这个对应的web项目的文件夹里的work\Catalina\localhost目录下将会出现一个SESSIONS.ser文件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IeiQEMAm-1670486882149)(Cookie&Session&JSP入门/image-20221203100224936.png)]
但是你idea里点击运行,他会做一个事情,他会把work文件夹删除了,然后重新生成一个新的文件夹,所以你前面钝化的SESSIONS.ser文件将会找不到了。所以你活化将会失败。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OoWlxoIr-1670486882150)(Cookie&Session&JSP入门/image-20221203230524993.png)]
打开原来位置看,会发现找不到那个SESSIONS.ser文件,所以活化会失败。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4sxoy0aX-1670486882150)(Cookie&Session&JSP入门/image-20221203100507593.png)]
但是其实没有关系,以后我们把服务器的项目部署到服务器端,服务器端又不是用idea来运行tomcat的,服务器端是直接用tomcat来启动我们的web项目的(只要我们把web项目放在tomcat的那个webapps中,那么服务器端的tomcat只要启动,我们的web项目就被部署,然后我们就浏览器就可以去访问那个web项目的资源了,那个资源的代码也可以成功被执行,根本不用idea。我们只要部署web项目到服务器端,然后我们浏览器去访问就行,都不用idea什么的),所以他的钝化和活化都可以正常执行的。所以你服务器重启了,你只要客户端还是用一个键名为JSESSIONID的cookie的id去访问服务器的资源,还是可以拿到一样的数据的,即最终效果还是和你服务器一直开着一样的。只是你要打印一下服务器重启前后用这个id拿到的对象的地址,你会发现两次打印的地址不同而已。
-
session什么时候被销毁?
有三个情况下服务器会把session销毁
-
服务器关闭
-
session对象调用invalidate() 。这个方法是HttpSession里面的方法。这个方法的作用就是自己销毁自己这个对象,哪个Session对象调用这个方法就销毁他自己。
-
session默认失效时间 30分钟。即服务器要是看到session最近30分钟都没有被使用,服务器就会自动把这个对象从内存里销毁。
这个默认失效的时间我们可以在tomcat安装目录的conf/web.xml文件中设置。(这个web.xml是所有项目的父配置文件,即你一个项目没有自己设置自己的web.xml就会用这个web.xml的设置来当他项目的配置的,所以我们只要改了这个web.xml就可以修改这个tomcat下所有的项目的session失效时间,这个截图了写了30是指session在不活动的状态30分钟这个session对象就会在服务器内存里面被自动的销毁的意思,单位是分钟。当然哈,你可以给你自己的web项目写一个配置来覆盖web.xml的父配置文件,这样你那一个web项目就可以有自己的默认销毁时间了,你修改那个你自己写的配置文件,就可以不会影响到整个tomcat下的web项目了)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YZHrNOsi-1670486882151)(Cookie&Session&JSP入门/image-20221203103222096.png)]
-
-
session的特点,知道了session的特点我们就知道在什么情况下适合使用session了。
- session用于存储一次会话的多次请求的数据,且数据存在服务器端
- session可以存储任意类型,任意大小的数据
-
session与Cookie的区别:
- session存储数据在服务器端,Cookie在客户端
- session没有数据大小限制,Cookie有。一般Cookie在浏览器里只存不超过4kb的数据,且存的Cookie键值对数据不超过20条(不同浏览器不同)。
- session数据安全,Cookie相对于不安全。浏览器中的数据很容易被删除,也很容易被别人查看到。
-
练习案例
-
案例需求:
- 用户访问带有验证码的一个登录页面login.jsp
- 然后用户输入用户名,密码以及验证码。
- 如果用户名和密码输入有误,跳转登录页面,提示:用户名或密码错误
- 如果验证码输入有误,跳转登录页面,提示:验证码错误
- 如果全部输入正确,则跳转到主页success.jsp,显示:用户名,欢迎您
-
分析思路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HYAk1slF-1670486882152)(Cookie&Session&JSP入门/image-20221205152208429.png)]
-
注意事项:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cq6y8XqW-1670486882153)(Cookie&Session&JSP入门/image-20221205145235160.png)]
-
代码
login.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>login</title> <script> window.onload = function(){ document.getElementById("img").onclick = function(){ this.src="/day16/checkCodeServlet?time="+new Date().getTime(); } } </script> <style> div{ color: red; } </style> </head> <body> <form action="/day16/loginServlet" method="post"> <table> <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="text" name="checkCode"></td> </tr> <tr> <td colspan="2"><img id="img" src="/day16/checkCodeServlet"></td> </tr> <tr> <td colspan="2"><input type="submit" value="登录"></td> </tr> </table> </form> <%-- 下面要是不用三元运算符来判断的话,你直接这么写<div><%=request.getAttribute("cc_error")%></div>,那么你一开始没有登入的时候,登入界面会显示null这个字符串--%> <%-- <div><%=request.getAttribute("cc_error")%></div>--%> <%-- <div><%=request.getAttribute("login_error")%></div>--%> <div><%=request.getAttribute("cc_error") == null ? "" : request.getAttribute("cc_error")%></div> <div><%=request.getAttribute("login_error") == null ? "" : request.getAttribute("login_error") %></div> </body> </html>
success.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1><%=request.getSession().getAttribute("user")%>,欢迎您</h1> </body> </html>
CheckCodeServlet类:
package cn.servlet; import javax.imageio.ImageIO; 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 java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; @WebServlet("/checkCodeServlet") public class CheckCodeServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int width = 100; int height = 50; //1.创建一对象,在内存中图片(验证码图片对象) BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); //2.美化图片 //2.1 填充背景色 Graphics g = image.getGraphics();//画笔对象 g.setColor(Color.PINK);//设置画笔颜色 g.fillRect(0,0,width,height); //2.2画边框 g.setColor(Color.BLUE); g.drawRect(0,0,width - 1,height - 1); String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789"; //生成随机角标 Random ran = new Random(); StringBuilder sb = new StringBuilder(); for (int i = 1; i <= 4; i++) { int index = ran.nextInt(str.length()); //获取字符 char ch = str.charAt(index);//随机字符 sb.append(ch); //2.3写验证码 g.drawString(ch+"",width/5*i,height/2); } String checkCode_session = sb.toString(); //将验证码存入session request.getSession().setAttribute("checkCode_session",checkCode_session); //2.4画干扰线 g.setColor(Color.GREEN); //随机生成坐标点 for (int i = 0; i < 10; i++) { int x1 = ran.nextInt(width); int x2 = ran.nextInt(width); int y1 = ran.nextInt(height); int y2 = ran.nextInt(height); g.drawLine(x1,y1,x2,y2); } //3.将图片输出到页面展示 ImageIO.write(image,"jpg",response.getOutputStream()); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } }
LoginServlet类:
package cn.servlet; 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("/loginServlet") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.设置request编码 request.setCharacterEncoding("utf-8"); //2.获取参数 String username = request.getParameter("username"); String password = request.getParameter("password"); String checkCode = request.getParameter("checkCode"); //3.先获取生成的验证码 HttpSession session = request.getSession(); String checkCode_session = (String) session.getAttribute("checkCode_session"); //删除session中存储的验证码 session.removeAttribute("checkCode_session"); //3.先判断验证码是否正确 if(checkCode_session!= null && checkCode_session.equalsIgnoreCase(checkCode)){ //忽略大小写比较 //验证码正确 //判断用户名和密码是否一致 if("zhangsan".equals(username) && "123".equals(password)){//其实这里需要调用UserDao查询数据库。这里我们就简单地写写,把所以我们这里就用字符串来先写了。 //登录成功 //存储信息,用户信息 session.setAttribute("user",username); //重定向到success.jsp response.sendRedirect(request.getContextPath()+"/success.jsp"); }else{ //登录失败 //存储提示信息到request request.setAttribute("login_error","用户名或密码错误"); //转发到登录页面 request.getRequestDispatcher("/login.jsp").forward(request,response); } }else{ //验证码不一致 //存储提示信息到request request.setAttribute("cc_error","验证码错误"); //转发到登录页面 request.getRequestDispatcher("/login.jsp").forward(request,response); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }