在上一文中,我们详细的讨论了会话、会话技术,细致的分析了Cookie的作用和使用方法。我们知道,Cookie是用于在浏览器中保存会话信息的,并且可以在多次Http请求之间实现数据的共享。但是如果每次请求携带的Cookie信息过多,则会明显的增大服务器端程序的处理难度和降低传输效率。

鉴于上面提出的问题,有请我们今日的主角—Session。

​注:关于会话、会话技术的描述请参考上篇博客。


会话技术之Session详解_客户端


1.Session的概念

Session同样也是会话技术的一种,和存储于客户端的Cookie不同,Session是存储于服务器端的。我们可以先简单的把Session理解为个人的储物柜,每个人有一个唯一对应的储物柜,用户可以根据手里的钥匙来操作储物柜。


会话技术之Session详解_客户端_02


就在上面的例子中,比如说是一家健身房,当我们进入健身房后,前台小妹会给我们一把储物柜的钥匙(Cookie),我们拿着钥匙找到对应的柜子(Session),换装,健身…,换装,归还钥匙,此次健身结束。

在上面这次健身经历中,从我们进入健身房到离开健身房就是一次完整的会话,前台小妹给我们的钥匙就是一个Cookie,钥匙上号码牌对应的柜子就是Session,每位健身者都只拥有一个储物柜,可以将衣服、背包、手机等物品存放其中,中间健身过程中比如买了瓶水、炸鸡等物品,一样可以放入储物柜中或拿在身边,这些内容即是会话过程中产生的数据,可以存储于客户端或服务器。

想象一下,如果没有储物柜的存在(Session),我们则需将所有衣服、鞋子等等带在身边,每换一个项目就得将所有的东西一起携带过去,这也是我们说的如果把过多的信息存储在客户端的弊端。

2.如何获取Session

在上面的例子中,我们讲到,一把钥匙对应一个储物柜。在Web应用中,一个客户端在服务器上也有一个对应这的Session,在当前会话结束前,这个Session被此客户端独享,当会话结束后,此Session会被销毁,内存会被回收。这个过程类似于健身房中储物柜的使用,使用结束归还钥匙,一个储物柜在不同的时间可以供多个健身者使用。

Session的获取主要通过HttpServletRequest来完成的,我们来看下request中提供的两个方法:

//如果create为true,如果与request对应的Session不存在,则返回一个新创建的Session;
//如果create为false,如果与request对应的Session不存在,则返回null;
//如果与request对应的Session存在,则直接返回该Session,create的值true或false,效果相同
public HttpSession getSession(boolean create);

//等价于getSession(true)
public HttpSession getSession();

上面两个方法有提到,与request对应的Session,这句话应该如何理解呢?我们知道,当浏览器发起请求时,请求参数,Cookie等信息都被封装在HttpServletRequest中,即request中包含客户端的Cookie信息(钥匙),当通过request去获取Session时,就会从request中得到钥匙信息,来判断当前服务器中是否有与之对应的Session存在。

下面我们通过一个小示例来看下,request是怎么通过Cookie来找到与此次请求相关的Session的。我们新建一个SessionServlet,urlPattern默认,其中的doGet方法如下:

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

//获取Session
HttpSession session = request.getSession();
//输出SessionId
System.out.println("服务器端request的对应的SessionId:" + session.getId());
}

浏览器中输入URL:http://localhost:8080/FirstProject/SessionServlet,访问此Servelt后,我们打开Chrome的控制态F12->Application->Storage->Cookies->http://localhost:8080(根据你输入的url来定),截图如下:


会话技术之Session详解_session_03


后端的运行截图如下,我们可以看到,服务器端Session的sessionId属性和浏览器端保存的Cookie(name为JSESSIONID)的值是相同的。这里可不是一个巧合,而是一个非常精妙的设计,我们在上面一直提到一个开储物柜的钥匙是一个Cookie,更精确的来讲,是一个name=JSESSIONID的Cookie,其中存储的值我们就可以理解成号码牌,这个号码牌唯一对应一个Session(储物柜,其编号为sessionId)。


会话技术之Session详解_java_04


这里还有一点让人比较疑惑的地方,我在服务端并没有创建一个name=JSESSIONID的Cookie呀,也没有添加任何Cookie到response中,怎么在Chrome中的是有的这个JSESSIONID这个Cookie?这里主要是因为,如果通过request获取Session时,如果新建了一个Session,则会自动创建一个Cookie,并将其添加到response中。

下图为第一访问http://localhost:8080/FirstProject/SessionServlet时,此次Http请求对应的响应头消息,当我们刷新页面后,可以看到,响应头中没有了Cookie信息。


会话技术之Session详解_服务器_05

会话技术之Session详解_客户端_06


3.HttpSession API

和Cookie相同,Tomcat在Servlet Api中提供了一个HttpSession接口,其中提供一些方法,让我们可以方便的操作Session,比如存储会话数据,设置一些属性等。

下面我们来看下HttpSession提供的方法:


会话技术之Session详解_服务器_07


其中的setAttribute、getAttribute、removeAtrribute方法是用来向Session这个储物柜中存取数据的,Session的也是一个域对象。

我们还可以通过setMaxInactiveInterval方法,来设置Session的空闲超时间隔,默认的空闲超时间隔为30分钟。这里简单的解释下空闲超时间隔,比如客户端A在9点登入系统,创建了对应的Cookie和Session,此时对应Session的失效时间为9点30;如果用户在9点25时,在页面上向服务器发起了请求,那么对应Session的过期时间则变为9点55;如果在接下来的半小时,也就是9点55之前,客户端没有向服务器发起任何请求,则此Session会自动被销毁,解绑所有的绑定在此Session上的属性。如果想修改当前应用中所有Session的默认超时间隔,则可以在web.xml中增加如下配置:

<session-config>
<session-timeout>30</session-timeout>
</session-config>

​在web.xml中配置的最大空闲超时间隔的单位是分钟,默认配置为30分钟。当session-timeout的值为0或者负数时,表示会话永不超时。不过不建议这么设置,因为Session中空闲超时时间存在的目的,就是可以让服务器能自动回收那些近期不在活跃的客户端对应的Session,这样才能保证内存资源不会因为积累大量的不活跃Session而耗尽。当然,当用户点击退出,主动结束会话时,我们同样可以使用​​invalidate​​方法来立即回收此Session。

4.Cookie与Session的区别与联系

这部分属于面试时经常问到的问题,因此这里也简单的进行一下总结。

区别:Cookie是存储于客户端(浏览器)的,可以通过Http响应头和JavaScript存储到浏览器端;Session是存储在服务器端的,可以通过request来获取Session;一个客户端中一个domain下的Cookie的数量是有限的,尽量数量不超过50个;服务器端总的Session数量不限,只要内存空间允许即可,但一个客户端唯一对应一个Session;Cookie中的值只能是字符串,Session中绑定的属性为Object,可以存储任意的数据类型;Cookie的安全性低于Session。

联系:客户端存储的Cookie中,存在一个名为JSESSIONID的Cookie,用于唯一对应服务端的一个Session(当Session没有失效,并且没有被invalidate时);当获取Session时,如果是新建一个Session,会自动向客户端发送JSESSIONID这个Cookie,如果客户端已经存在,则更新此Cookie。

上面技术两者之间的区别与联系了,其实Cookie和Session都是会话技术的一种,其出现的目的就是为了方便会话信息的追踪,但是这两者之间有可通过JSESSIONID来关联到一起,解决"一人一柜"的独立性需求,可以说是很精髓了。

浏览器中的Cookie,如果在下次请求时自动将domain、path都符合Cookie自动装入request Header中,可在服务端通过request获取传来的Cookie,同样可以通过request获取与客户端唯一对一个的Session;而Session的功能就像是一个储物柜,客户端可以通过JSESSIONID这把钥匙找到session,我们可以将数据存储在此柜子中。

我们在上面的例子中,Session的功能完全没有被体现出来,因为只做一个柜子的话,与申请一块内存直接存储信息相比,Session似乎没有什么飞跃性的优化,如果单独的讲Session,这里是这样的,功能很鸡肋,但是,如果是在JSP中,Session就是一个bug性的功能了,因为Session是JSP的内置对象,这么来理解吧,Session这个储物柜可以被你携带到JSP页面中,你可以在JSP页面中直接从Session中获取数据。想想吧,这样子直接省去了JS对数据的解析等问题,多么美好啊。

5.总结

Session技术的出现,极大的促进了Web的发展,让JSP开发变得更简单快捷,再加上EL表达式对Session的加持,Session变得更加强劲。

不过,现在的趋势是前后端分离,JSP的使用率目前非常低咯,估计再过个十年就要退出历史舞台了。和JSP荣誉与共的Session,也难逃此命运。现在更多的是用token技术,比如JWT(Json Web Token),让存储于客户端的信息更安全,而Session的储物柜的功能则可使用文件存储、数据库(关系型数据库,MySQL、Oracle等)、Redis等等来进行替代。

江山代有才人出,各领风骚数百年。吾辈共勉!

参考阅读:


  1. ​Servlet基础之HttpServletRequest详解​
  2. ​Servlet基础之HttpServletResponse详解​
  3. ​会话技术之Cookie详解​


又到了分隔线以下,本文到此就结束了,本文内容全部都是由博主自己进行整理并结合自身的理解进行总结,如果有什么错误,还请批评指正。

有任何疑问,可以评论区留言。