javaWeb项目之Servlet学习笔记----初识session

https://www.cnblogs.com/fjdingsd/p/4905459.html

Session是服务器端技术

利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,注意是默认情况下,一个浏览器独占一个session,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自数据存放在各自的session中,当用户再去访问服务器的其他web资源时,其他web资源再从用户各自的session中取出数据为用户服务。

Session和Cookie的主要区别:

  Cookie技术是客户端技术,是由服务器将用户的数据写回给用户浏览器的。
  Session技术是服务器端技术,是把用户的数据写到服务器为用户所创建的独享的Session对象中,但Session是基于Cookie的

Session对象由服务器创建,这跟Cookie不一样。编程人员可以通过request请求对象的getSession()方法获取Session对象。

  注意,Session一旦创建,是会存活无操作情况(或者浏览器关闭)下30分钟,这是由服务器(如Tomcat)来指定的。当我们关闭浏览器后,因为Session是基于cookie的,因此Session是存在的,只是我们可能无法再使用到(要看cookie是否还能取到)。

  那么我们以一个简单的Session例子来说明,创建两个Servlet:分别命名为ServletDemo1和ServletDemo2。

在ServletDemo1中的代码如下:

1 HttpSession session = request.getSession();
2 String data = "message form SessionDemo1";
3 session.setAttribute("data", data);

在ServletDemo2中的代码如下:

response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
        
HttpSession session = request.getSession();
String data = (String) session.getAttribute("data");
writer.write(data);

  当我们打开一个浏览器先访问ServletDemo1,此时浏览器会帮我们创建Session对象,并保存数据,这时再访问ServletDemo2可以看到该保存的数据:

  

  说明Session确实能保存访问不同Servlet时的数据。

  这里说清楚一点,虽然代码都是相同的,但是针对不同浏览器访问的话看到的是不同的Session对象保存的不同的数据,虽然这时候数据都是一样的。比如说A在自己主机上访问这个Servlet和B在自己电脑上访问这个相同的Servlet,A和B得到各自的Session。

  而如果我们打开一个浏览器访问ServletDemo1后,再打开另一个浏览器访问ServletDemo2,那么则会显示该数据找不到:

  

注意:这时候是两个浏览器,即已经是两个会话了!!!

 

 

在上一篇中我们学习了Session对象默认在一个会话过程中,由服务器创建,能保存在这个会话过程中用户访问多个web资源时产生的需要保存的数据,并在访问服务器中其他web资源时可以将这些数据从Session中取出并为之使用。

  然而在一个会话过程后就将无法再取到Session对象(因为cookie不存在了,原因下面将阐明),可能导致的是用户误将浏览器关闭而使保存在Session中的数据无法再取到,因此如果我们能将多个会话同时使用同一个Session对象就能满足用户“刁蛮”的需求。

  在前一篇的最开始就说过,在默认情况下,一个浏览器独享一个Session。请注意,这里是默认情况下, 而我们有了新需求,就要打破默认情况(比如默认情况下Cookie也是在一个会话结束后消失,但是我们设置了setMaxAge后就能在硬盘中生存),做到多个浏览器共享一个Session,同时这也意味着一台用户主机有一个Session,而不管他开了多少个浏览器。

  那么首先我们必须先了解Session的底层工作原理。我们还是先看在一个会话过程中,同一个浏览器在访问多个web资源的情况好了,大致分为以下几个步骤:

  1,浏览器访问某个Servlet,这时如果服务器要从请求对象中获取Session对象(第一次获取也是创建),那么服务器会为这个Session对象创建一个id:JSESSIONID

  2,同时在对浏览器的响应过程中,这个Session会将JSESSIONID这个id以Cookie形式回送给客户端浏览器,记住,这时候Cookie服务器没有设置有效时间,因此是存在浏览器的缓存中,而不是在硬盘文件。

  3,当用户继续在这个会话过程中访问其他Servlet,这时候这个Servlet再从请求对象中获取Session对象,注意这时候获取Session对象是从浏览器发来的请求中查询是否有名为JSESSIONID的这个Cookie,如果有,那么这个Session就不用再创建,而是直接根据查询服务器中这个相同JSESSIONID值的Session,换句话说就可以取得之前存在这个Session中的数据。

  总结来说,Session是基于Cookie的。

  (注:cookie并不是万能的,Session首先是依据cookie,但是有时候cookie不能用,这时候Session会查询发来请求的URL地址是否有JSESSIONID,具体请看《Servlet的学习之Session(3)》。)

  我们可以通过下面这个动图来看一看:

  

  对于这个Session的隐藏Cookie,我们可以做个小实验来验证下,在【myservlet】这个web工程下创建两个Servlet,分别命名为SessionDemo1和SessionDemo2:

在SessionDemo1代码为:

1     HttpSession session = request.getSession();
2     String data = "Message from SessionDemo1";
3     session.setAttribute("data", data);

在SessionDemo2代码为:

1     HttpSession session = request.getSession();
2     System.out.println((String)session.getAttribute("data"));

我们在浏览器中打开HttpWatch,来访问SessionDemo1,因为是首次访问Servlet,查看SessionDemo1给浏览器的响应:

  

确确实实服务器发送回浏览器有这个JSESSIONID名称的Cookie,这时候如果我们再在打开的浏览器去访问SessionDemo2,那么在HttpWatch中观察请求包的内容发现:

  

再次访问服务器时,浏览器就会带着这个名为JSESSIONID的Cookie给服务器,服务器正是通过这个cookie中的JSESSIONID值去服务器中查找之前为该浏览器创建的Session。

  如果我们将浏览器关闭,由于这个cookie没有设置“setMaxAge”,因此这个cookie只存在于浏览器的缓冲,浏览器关闭即被销毁。如果想使关闭浏览器之后,Session还能存在,我们就要人为的覆盖这个Session的cookie,并设置覆盖cookie的有效时间和有效路径。而这个cookie的值,也就是JSESSIONID的值,可以通过Session的getId()方法得到。

  1,覆盖有效时间:

  注意,服务器在为浏览器创建Session后,在用户没有操作的情况下(或者浏览器关闭后)默认为其维护30分钟。这点可以从Tomcat的【web.xml】文件中可以看出:

  

当然我们从这里也可以修改服务器默认的销毁无操作的Session时间。

  当然如果我们不要全局设置所有服务器中Session的销毁时间,就在每个web应用中的web.xml文件中自定义添加<session-config>和<session-timeout>进行设置。

  注:我们还可以通过Session对象的invalidate()方法,将某个Session进行立刻销毁。

  对此,如果我们要覆盖一个Session的cookie并保存在硬盘文件中,我们设置的cookie有效时间就不要超过服务器默认的session-timeout时间。

  2,覆盖有效路径:

  我们在《Servlet的学习之Cookie》中知道,如果我们创建一个Cookie对象,没有设置“setPath”,那么Cookie的有效路径为创建该Cookie的程序(通常为某个Servlet),即只有访问了这个程序时浏览器才会带着Cookie过去,那实在是“人脉不通”,访问这个web应用的其他资源就无法再使用Session了。

  我们看看刚才的第一次访问Servlet时,服务器为浏览器创建的Session中的cookie的有效路径:

  

可以看到这个服务器默认将JSESSIONID这个cookie的有效路径设置为创建这个Session的web工程根目录。所以我们要覆盖Session中的cookie时也应该设置路径为该web工程根目录。

  好,接下来对上面那个Servlet的例子进行改造,我们只需要在SessionDemo1中修改就行,因为这个首次将Session的cookie返回给客户端,修改后代码如下:

HttpSession session = request.getSession();
    String data = "Message from SessionDemo1";
    session.setAttribute("data", data);
        
    Cookie cookie = new Cookie("JSESSIONID", session.getId());
    cookie.setMaxAge(30*60);
    cookie.setPath("/myservlet");
    response.addCookie(cookie);

这样,当我们打开浏览器访问了SessionDemo1之后,就能在存放cookie的目录中找到该cookie,如果我们通过HttpWatch来查看可以看到重名的这个cookie:

  

虽然JSEESIONID这个cookie重名了,没有关系,因为其值都是一样的,并且如果我们将浏览器关闭后,没有设置cookie有效时间的(也是原先Session发来的)cookie将不复存在(存在浏览器缓存中,浏览器关闭就被销毁),这时重新打开一个浏览器,再去访问SessionDemo2依然能获取到原来Session中保存的内容:

  

注意,这是另外打开浏览器窗口访问的SessionDemo2!!另附:

  

  通过这里我们可以看到,我们人为地将原先Session定义的cookie给替换了,而Session并不知道,只要能获得“JSESSIONID”这个cookie,它就认为cookie是存在的,可以从这个cookie中id值获取以前保存的信息,因此我们实现了一台主机共享一个Session,此时,当浏览器关闭,或者说结束一个会话后,依然能获取Session来获取之前保存的数据。

 

但不是所有的用户的浏览器都会保持着接收Cookie,当有些用户的浏览器禁用Cookie或者第三方安全工具阻止了Cookie之后,那么Session就再也无法保存数据了。

  我们通过IE浏览器的【工具】--->【Internet选项】--->【隐私】--->【高级】,勾选“替代自动cookie处理”,将“第一方Cookie”和“第三方Cookie”都选择为“阻止”。这样就阻止了Cookie。如果我们再去访问会创建的Session的Servlet,那么就会看到如下提示:  

  

  当浏览器禁用Cookie之后,即使是在一个浏览器的一个会话过程中,Session都不起任何作用,因为这时候浏览器连要缓存在IE浏览器中Cookie都不接收。所以每次调用request.getSession()方法都是创建新的Session。其实,getSession()这个方法首先是Session查询从浏览器是否能发来包含JSESSIONID的cookie;如果没有这个cookie,那么浏览器会查询请求URL,这个请看下一段。如果两种方式都没有能找到以前的Session,那么getSession()最终会创建新的Session。

  如果我们想在浏览器禁用Cookie之后能继续使用同一个Session,那么Cookie不能用,我们就要从Session使用Cookie的原理来解决。Session使用Cookie的原理在于能保存JSESSIONID,而我们如果在点击的超链接的URL中加入JSESSIONID的值,那么调用request.getSession()方法的Servlet就可以直接从该URL获取JSESSIONID,再去配对服务器端相同JSESSIONID的值的Session,这样就可以再次获取服务器端Session中保存的数据。这个在超链接中加入JSESSIONID的方法叫做URL重写。URL重写,用来解决客户端浏览器禁用Cookie之后Session的共享问题。

URL重写主要有两个方法:

  Response.encodeURL()方法

  Response.encodeRedirectURL()方法

请认真阅读这两个方法的API文档:

  

  

  如果是在一个页面中的可以点击的超链接要使用encodeURL()方法,而在一个Servlet中如果有要重定向的路径必须使用encodeRedirectURL()方法,而不能使用encodeURL()方法。无论哪种方法,经过URL重写之后都会在URL路径的最后增加JSESSIONID和值。

  我们再将前篇《Servlet的学习之Session(1)》中最后的购物车例子进行改写,使得当用户浏览器禁用cookie之后依然能完成功能。

  当我们禁用cookie之后,再使用这个例子,那么浏览器会抛出空指针异常,原因在于最后购物车Servlet获取我们存放物品的LinkedList上,因为禁用了cookie,使得我们无法获得上一次的Session,因此每次使用getSession()方法都要获取新的Session,而新的Session中并不会有我们设置的“productCart”这个属性,因此每次获得的LinkedList其实都不存在:

复制代码

1     LinkedList<Book> link = (LinkedList<Book>) session.getAttribute("productCart");
2         
3     writer.write("您所希望购买的商品如下:<br>");
4     for(ListIterator<Book> literator = link.listIterator();literator.hasNext();){
5             Book b = literator.next();
6             writer.write(b.getName()+"<br>");
7         }

复制代码

那么我们就需要在购物车Servlet的前一个Servlet,也就是进行重定向到购物车Servlet的那个Servlet中,将重定向的URL地址进行重写:

1     String reUrl = response.encodeRedirectURL("/SessionProject/servlet/SessionDemo3");
2     response.sendRedirect(reUrl);

只要简单使用encodeRedirectURL或者encodeURL方法就能将地址增加JSESSIONID:

  

记住,鉴于用户浏览器会禁用cookie,因此我们需要在整个web工程中对于会使用到getSession()方法的超链接、重定向或者转发等等的URL地址都要进行URL重写。

  同时!!在所有Servlet的中,最开始的Servlet,也可以称为首页Servlet的代码的最开始加入request.getSession(); 这样一句“废话”,这是因为将URL重写的前提是必须要有Session,或者说已经建立了Session。

  这里将《Servlet的学习之Session(1)》使用URL改写后的例子重新贴出代码,这里略去商品的bean类和使用Map作为数据库的代码,请到《Servlet的学习之Session(1)》博客查阅:

在首页展示商品的Servlet:

response.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=utf-8");
    PrintWriter writer = response.getWriter();
        
    request.getSession();
        
    Map<String,Book> map = BookDatabaseFactory.getMap();
    for(Map.Entry<String, Book> en : map.entrySet()){
            
    String reUrl = response.encodeURL("/SessionProject/servlet/SessionDemo2?id="+en.getKey());
    writer.write(en.getValue().getName() +
            "<a href='"+reUrl+"' target='_blank'>购买</a> <br>");
        }

///

String bookId = request.getParameter("id");
    Map<String,Book> map = BookDatabaseFactory.getMap();
    Book book = map.get(bookId);
        
    HttpSession session = request.getSession();
    LinkedList<Book> link = (LinkedList<Book>) session.getAttribute("productCart");  //禁用cookie之后这里会空指针异常
    if(link == null) {  
        link = new LinkedList<Book>();    
        session.setAttribute("productCart", link);
    }
    link.add(book);
    //将所点击的商品交给购物车页面显示
    String reUrl = response.encodeRedirectURL("/SessionProject/servlet/SessionDemo3");
    response.sendRedirect(reUrl);
//
response.setCharacterEncoding("utf-8");
    response.setContentType("text/html;charset=utf-8");
    PrintWriter writer = response.getWriter();    
        
    HttpSession session = request.getSession();
    LinkedList<Book> link = (LinkedList<Book>) session.getAttribute("productCart");  //禁用cookie之后这里会空指针异常
        
    writer.write("您所希望购买的商品如下:<br>");
    for(ListIterator<Book> literator = link.listIterator();literator.hasNext();){
    Book b = literator.next();
    writer.write(b.getName()+"<br>");
    }
        /*    当用户禁用cookie之后再使用覆盖cookie就不能用了
         * //当浏览器关闭后重新打开依然有之前想要购买的物品
        Cookie cookie = new Cookie("JSESSIONID", session.getId());
        cookie.setMaxAge(60*60);
        cookie.setPath("/SessionProject");
        response.addCookie(cookie);*/

  通过以上两个地方的URL重写(已用红色字体标出),我们就能在用户禁用cookie之后继续能获得之前保存数据的Session,从而对该Session中的数据继续进行操作。我们甚至可以从首页查看源文件看到所有的超链接中可以看到都加入了JSESSIONID:

  

但是,这个方法只能在一个会话过程中有效。也就是说当用户将浏览器关闭之后,重新打开,之前的数据就不复存在了,因为这种方法无法在新的会话中获取之前的Session。因此不能像《Servlet的学习之Session(2)》中使用cookie方法一样无论多少个会话都能获取之前相同的Session。

补充:上面的例子我们以用户禁用cookie为前提,因此在首页显示商品的页面上所有的超链接我们都进行URL重写,这点我们从页面查看源文件也能看出。如果我们上面的例子在用户没有禁用cookie的时候,第一次访问时,我们查看源文件,所有的超链接依然还是有URL重写后将所有的超链接都附上JSESSIONID;但是我们再次刷新时,这些JSESSIONID都会消失:

  

  这是因为服务器很“聪明”,只要你没有禁用cookie,它就不需要进行URL重写,准确的说是进行了URL重写,但是因为没有禁用cookie,因此重写后的URL还是跟原来一样,这点在最开始的encodeRedirectURL和encodeURL的API截图中也可以看出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值