根据设计,HTTP是一种无状态的协议。它意味着Web应用并不了解有关同一用户之前请求的信息。维持会话状态信息的方法之一是使用Servlet或者JSP容器提供的会话跟踪功能。Servlet API规范定义了一个简单的HttpSession接口,经过它咱们能够方便地实现会话跟踪。 java
HttpSession接口提供了存储和返回标准会话属性的方法。标准会话属性如会话标识符、应用数据等,都以“名字-值”对的形式保存。简而言之,HttpSession接口提供了一种把对象保存到内存、在同一用户的后继请求中提取这些对象的标准办法。在会话中保存数据的方法是setAttribute(String s, Object o),从会话提取原来所保存对象的方法是getAttribute(String s)。 web
每当新用户请求一个使用了HttpSession对象的JSP页面,JSP容器除了发回应答页面以外,它还要向浏览器发送一个特殊的数字。这个特殊的数字称为“会话标识符”,它是一个惟一的用户标识符。此后,HttpSession对象就驻留在内存之中,等待同一用户返回时再次调用它的方法。 浏览器
在客户端,浏览器保存会话标识符,并在每个后继请求中把这个会话标识符发送给服务器。会话标识符告诉JSP容器当前请求不是用户发出的第一个请求,服务器之前已经为该用户建立了HttpSession对象。此时,JSP容器再也不为用户建立新的HttpSession对象,而是寻找具备相同会话标识符的HttpSession对象,而后创建该HttpSession对象和当前请求的关联。 服务器
会话标识符以Cookie的形式在服务器和浏览器之间传送。若是客户端不支持cookie,运用url改写机制来保证会话标识符传回服务器。 cookie
2、 Session事件侦听 session
HttpSessionBindingEvent类\
定义\
public class HttpSessionBindingEvent extends EventObject
这个事件是在监听到HttpSession发生绑定和取消绑定的状况时连通HttpSessionBindingListener的。这多是一个session被终止或被认定无效的结果。
事件源是HttpSession.putValue或HttpSession.removeValue。
构造函数
public HttpSessionBindingEvent(HttpSession session, String name);
经过引发这个事件的Session和发生绑定或取消绑定的对象名构造一个新的HttpSessionBindingEvent。
方法
1、getName
public String getName();
返回发生绑定和取消绑定的对象的名字。
2、getSession
public HttpSession getSession();
返回发生绑定和取消绑定的session的名字。
HttpSessionBindingListener接口
定义\
public interface HttpSessionBindingListener
这个对象被加入到HTTP的session中,执行这个接口会通告有没有什么对象被绑定到这个HTTP session中或被从这个HTTP session中取消绑定。
方法
1、valueBound
public void valueBound(HttpSessionBindingEvent event);
当一个对象被绑定到session中,调用此方法。HttpSession.putValue方法被调用时,Servlet引擎应该调用此方法。
2、valueUnbound
public void valueUnbound(HttpSessionBindingEvent event);
当一个对象被从session中取消绑定,调用此方法。HttpSession.removeValue方法被调用时,Servlet引擎应该调用此方法。
app
Session的事件处理机制与swing事件处理机制不一样。Swing采用注册机制,而session没有;当任一session发生绑定或其余事件时,都会触发HttpSessionBindingEvent ,若是servlet容器中存在HttpSessionBindingListener的实现类,则会将事件做为参数传送给session侦听器的实现类。在HttpSessionBindingEvent 中能够经过getsession获得发生绑定和取消绑定的session的名字,而侦听器能够据此作更多处理。 函数
所以,对session的事件侦听,只需实现HttpSessionBindingListener便可。 网站
从servlet2.3增长了 this
HttpSessionEvent(This is the class representing event notifications for changes to sessions within a web application)
HttpSessionActivationListener(Objects that are bound to a session may listen to container events notifying them that sessions will be passivated and that session will be activated.)
HttpSessionAttributeListener(This listener interface can be implemented in order to get notifications of changes to the attribute lists of sessions within this web application.)
分别执行不一样的任务,处理基本相同。
3、例子(zz)
捕获Session事件的意义:
1、 记录网站的客户登陆日志(登陆,退出信息等)
2、 统计在线人数
3、 等等还有不少,呵呵,本身想吧……总之挺重要的。
Session表明客户的会话过程,客户登陆时,往Session中传入一个对象,便可跟踪客户的会话。在Servlet中,传入Session的对象若是是一个实现HttpSessionBindingListener接口的对象(方便起见,此对象称为监听器),则在传入的时候(即调用HttpSession对象的setAttribute方法的时候)和移去的时候(即调用HttpSession对象的removeAttribute方法的时候或Session Time out的时候)Session对象会自动调用监听器的valueBound和valueUnbound方法(这是HttpSessionBindingListener接口中的方法)。由此可知,登陆日志也就不难实现了。
另一个问题是,如何统计在线人数,这个问题跟实现登陆日志稍微有点不一样,统计在线人数(及其信息),就是统计如今有多少个Session实例存在,咱们能够增长一个计数器(若是想存储更多的信息,能够用一个对象来作计数器,随后给出的实例中,简单起见,用一个整数变量做为计数器),经过在valueBound方法中给计数器加1,valueUnbound方法中计数器减1,便可实如今线人数的统计。固然,这里面要利用到ServletContext的全局特性。(有关ServletContext的叙述请参考Servlet规范),新建一个监听器,并将其实例存入ServletContext的属性中,以保证此监听器实例的惟一性,当客户登陆时,先判断ServletContext的这个属性是否为空,若是不为空,证实已经建立,直接将此属性取出放入Session中,计数器加1;若是为空则建立一个新的监听器,并存入ServletContext的属性中。
举例说明:
实现一个监听器:
// SessionListener.java
import java.io.*; import java.util.*; import javax.servlet.http.*;
//监听登陆的整个过程 public class SessionListener implements HttpSessionBindingListener {
public String privateInfo=""; //生成监听器的初始化参数字符串 private String logString=""; //日志记录字符串 private int count=0; //登陆人数计数器
public SessionListener(String info){ this.privateInfo=info; }
public int getCount(){ return count; }
public void valueBound(HttpSessionBindingEvent event) { count++; if (privateInfo.equals("count")) { return; } try{ Calendar calendar=new GregorianCalendar(); System.out.println("LOGIN:"+privateInfo+" TIME:"+calendar.getTime()); logString="\nLOGIN:"+privateInfo+" TIME:"+calendar.getTime()+"\n"; for(int i=1;i<1000;i++){ File file=new File("yeeyoo.log"+i); if(!(file.exists())) file.createNewFile(); //若是文件不存在,建立此文件 if(file.length()>1048576) //若是文件大于1M,从新建立一个文件 continue; FileOutputStream foo=new FileOutputStream("yeeyoo.log"+i,true); //以append方式打开建立文件 foo.write(logString.getBytes(),0,logString.length()); //写入日志字符串 foo.close(); break;//退出 } }catch(FileNotFoundException e){} catch(IOException e){} }
public void valueUnbound(HttpSessionBindingEvent event) { count--; if (privateInfo.equals("count")) { return; } try{ Calendar calendar=new GregorianCalendar(); System.out.println("LOGOUT:"+privateInfo+" TIME:"+calendar.getTime()); logString="\nLOGOUT:"+privateInfo+" TIME:"+calendar.getTime()+"\n"; for(int i=1;i<1000;i++){ File file=new File("yeeyoo.log"+i); if(!(file.exists())) file.createNewFile(); //若是文件不存在,建立此文件 if(file.length()>1048576) //若是文件大于1M,从新建立一个文件 continue; FileOutputStream foo=new FileOutputStream("yeeyoo.log"+i,true); //以append方式打开建立文件 foo.write(logString.getBytes(),0,logString.length()); //写入日志字符串 foo.close(); break;//退出 } }catch(FileNotFoundException e){} catch(IOException e){} }
} |
登陆日志的实现:
下面再来看看咱们的登陆Servlet中使用这个监听器的部分源代码:
…… HttpSession session = req.getSession (true); …… ////////////////////////////////////////////////////////////////// SessionListener sessionListener= new SessionListener("IP:"+req.getRemoteAddr()); //对于每个会话过程均启动一个监听器 session.setAttribute("listener",sessionListener); //将监听器植入HttpSession,这将激发监听器调用valueBound方法, //从而记录日志文件。 ////////////////////////////////////////////////////////////////// |
当系统退出登陆时,只需简单地调用session.removeAttribute(“listener”);
便可自动调用监听器的valueUnbound方法。或者,当Session Time Out的时候也会调用此方法。
登陆人数的统计:
ServletContext session1=getServletConfig().getServletContext(); //取得ServletContext对象实例 if((SessionListener)session1.getAttribute("listener1")==null) { SessionListener sessionListener1=new SessionListener("count"); //只设置一次,不一样于上面日志文件的记录每次会话均设置。 //即当第一个客户链接到服务器时启动一个全局变量, //此后全部的客户将使用相同的上下文。 session1.setAttribute("listener1",sessionListener1); //将监听器对象设置成ServletContext的属性,具备全局范围有效性, //即全部的客户都可以取得它的实例。 } session.setAttribute("listener1",(SessionListener)session1. getAttribute("listener1")); //取出此全局对象,而且将此对象绑定到某个会话中, //此举将促使监听器调用valueBound,计数器加一。 |
在此后的程序中随时能够用如下代码取得当前的登陆人数:
((SessionListener)session.getAttribute("listener1")).getCount() |
getCount()是监听器的一个方法,即取得当前计数器的值也就是登陆人数了。