跟踪客户状态
Web服务器跟踪客户状态通常有四种办法
其中第四种是我们研究的重点
Session的概念
Session用于跟踪客户的状态。Session指的是在一段时间内,单个客户与Web服务器的一连串交互过程。在一个Session中,客户可能会多次请求访问同一个网页,也有可能请求访问各种不同的服务器资源。
Session的例子
Session的运行机制
HttpSession接口
Session的生命周期
在以下情况中,Session将结束生命周期,Servlet容器会将Session所占用的资源释放掉
Session过期
Session过期是指当Session开始后,在一段时间内客户没有和Web服务器交互,这个Session会失效,HttpSession的setMaxInactiveInterval()方法可以设置允许Session保持不活动时间的状态(以秒为单位),如果超过这一时间,Session就会失效。
Session范例程序
创建一个简单的邮件系统,由3个JSP文件组成:
<%--maillogin.jsp --%> <%@ page contentType="text/html; charset=GB2312" pageEncoding="gb2312"%> <%@ page session="true" %> <html> <head> <title>session练习</title> </head> <body bgcolor="#FFFFFF" onLoad="document.loginForm.username.focus()"> <% String name=""; if(!session.isNew()){ name=(String)session.getAttribute("username"); if(name==null)name=""; } %> <p>欢迎光临邮件系统</p> <p>Session ID:<%=session.getId()%></p> <form name="loginForm" method="post" action="mailcheck.jsp"> <table width="500" border="0" cellspacing="0" cellpadding="0"> <tr> <td> <table width="500" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="401"><div align="right">User Name: </div></td> <td width="399"><input type="text" name="username" value=<%=name%>></td> </tr> <tr> <td width="401"><div align="right">Password: </div></td> <td width="399"><input type="password" name="password"></td> </tr> <tr> <td width="401"> </td> <td width="399"><br><input type="Submit" name="Submit" value="提交"></td> </tr> </table> </td> </tr> </table> </form> </body> </html> <%--mailcheck.jsp --%> <%@ page contentType="text/html; charset=GB2312" pageEncoding="gb2312"%> <%@ page session="true" %> <html> <head> <title> checkmail </title> </head> <body> <% String name=null; name=request.getParameter("username"); if(name!=null)session.setAttribute("username",name); %> <a href="maillogin.jsp">登录</a> <a href="maillogout.jsp">注销</a> <p>当前用户为:<%=name%> </P> <P>你的信箱中有52封邮件</P> </body> </html> <%--maillogout.jsp --%> <%@ page contentType="text/html; charset=GB2312" pageEncoding="gb2312"%> <%@ page session="true" %> <html> <head> <title> maillogout </title> </head> <body> <% maString name=(String)session.getAttribute("username"); session.invalidate(); %> <%=name%>,再见! <a href="maillogin.jsp">重新登录邮件系统</a> </body> </html>
三个页面显示情况如下(注意注销后,重新登录Session ID变化了,这是因为在maillogout中我们将其Session销毁了):
练习题
分配HttpSession ID是服务器的行为,而不是JavaWeb应用程序,因此C错;至于A,因为默认每一个页面开启Session,我们可以将Session属性设置为false,网页便不再支持Session。
观察者模式(Observer)
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,让他们能够自动更新自己。
观察者模式组成
一个实例:
package com.test.observer; public interface Subject { public void attach(Observer observer); public void detach(Observer observer); public void notifyObservers(); } package com.test.observer; public abstract class Observer { public abstract void update(Object object); } package com.test.observer; public class ConcreteObserver extends Observer { @Override public void update(Object object) { System.out.println("观察者更新自己"); System.out.println(object); } } package com.test.observer; import java.util.ArrayList; import java.util.List; public class ConcreteSubject implements Subject { List<Observer> list = new ArrayList<Observer>(); public void attach(Observer observer) { list.add(observer); } public void detach(Observer observer) { list.remove(observer); } public void notifyObservers() { for(Observer observer : list) { observer.update("通知观察者"); } } public void a() { this.notifyObservers(); } } package com.test.observer; public class Client { public static void main(String[] args) { ConcreteSubject subject = new ConcreteSubject(); ConcreteObserver o1 = new ConcreteObserver(); ConcreteObserver o2 = new ConcreteObserver(); ConcreteObserver o3 = new ConcreteObserver(); subject.attach(o1); subject.attach(o2); subject.attach(o3); subject.a(); subject.detach(o1); System.out.println("------------------"); subject.a(); } } /* output: 观察者更新自己 通知观察者 观察者更新自己 通知观察者 观察者更新自己 通知观察者 ------------------ 观察者更新自己 通知观察者 观察者更新自己 通知观察者 */
Listener
Listener是Servlet的监听器,它可以监听客户端的请求、服务端的操作等。通过监听器,可以自动激发一些操作,比如监听在线的用户的数量。当增加一个HttpSession时,就激发sessionCreted(HttpSessionEvent se)方法,这样就可以给在线人数加1。
常用的监听接口有以下几个:
HttpSessionListener监听HttpSession的操作。当创建一个Session时,激发sessionCreated(HttpSessionEvent se)方法;当销毁一个Session时,激发sessionDestroyed(HttpSessionEvent se)方法
一个实例:
package com.test.listener; import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeListener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class OnLineCountListener implements HttpSessionListener, ServletContextListener, ServletContextAttributeListener { private int count; // 用户数量 public void sessionCreated(HttpSessionEvent arg0) // 用户访问这个web应用下的任何一个页面或者JSP时,该方法自动被调用(观察者角色) { count++; setContext(arg0); System.out.println("count"); } public void sessionDestroyed(HttpSessionEvent arg0) { count--; setContext(arg0); } public void setContext(HttpSessionEvent se) { se.getSession().getServletContext().setAttribute("onLine", new Integer(count)); // 触发增加或者替换操作 } public void contextInitialized(ServletContextEvent arg0) { log("contextInitialized()"); } public void contextDestroyed(ServletContextEvent arg0) { log("contextDestroyed()"); } public void attributeAdded(ServletContextAttributeEvent arg0) { log("attributeAdded('" + arg0.getName() + "', '" + arg0.getValue() + "')"); } public void attributeRemoved(ServletContextAttributeEvent arg0) { log("attributeRemoved('" + arg0.getName() + "', '" + arg0.getValue() + "')"); } public void attributeReplaced(ServletContextAttributeEvent arg0) { log("attributeReplaced('" + arg0.getName() + "', '" + arg0.getValue() + "')"); } private void log(String message) { System.out.println("ContextListener: " + message); } }
在web.xml中配置以下信息:
<listener>
<listener-class>com.test.listener.OnLineCountListener</listener-class>
</listener>
服务器一启动,输出ContextListener: contextInitialized(),这是我们写的contextInitialized()方法被调用了。接着访问Web应用下的任何一个JSP页面,命令行输出ContextListener: attributeAdded('onLine', '1')
count,关闭浏览器在打开,访问JSP页面,输出:
ContextListener: attributeReplaced('onLine', '1')
count,可能我们会有一个疑惑,这里在线人数应该是2,这个问题和getValue方法实现是相关的,下面的文字摘自J2EE API:
public java.lang.Object getValue()
If the attribute was added, this is the value of the attribute. If the attribute was removed, this is the value of the removed attribute. If the attribute was replaced, this is the old value of the attribute.
可以看到被替换后的返回值为旧值。