第六部分_HttpSession、观察者模式与Listener详解

跟踪客户状态
  Web服务器跟踪客户状态通常有四种办法

  • 建立含有跟踪数据的隐藏字段(<input type="hidden" name="name1" value="value1">)
  • 重写包含额外参数的URL
  • 使用持续的Cookie
  • 使用Servlet API中的Session(会话)机制

其中第四种是我们研究的重点

Session的概念

Session用于跟踪客户的状态。Session指的是在一段时间内,单个客户与Web服务器的一连串交互过程。在一个Session中,客户可能会多次请求访问同一个网页,也有可能请求访问各种不同的服务器资源。

Session的例子

  • 在电子邮件应用中,从一个客户登录到电子邮件系统开始,经过收信、写信和发信等一系列操作,直至最后退出邮件系统,整个过程为一个Session。
  • 在购物网站应用中,从一个客户开始购物,到最后结账,整个过程为一个Session。

Session的运行机制

  • 当一个Session开始时,Servlet容器将创建一个HttpSession对象,在HttpSession对象中可以存放客户状态的信息(例如购物车)。
  • Servlet容器为HttpSession分配一个唯一标识符,称为Sessio ID。Servlet容器把Session ID作为Cookie保存在客户的浏览器中。
  • 每次客户发出HTTP请求时,Servlet容器可以从HttpServletRequest对象中读取Session ID,然后根据Session ID找到相应的HttpSession对象,从而获取客户的状态信息。

HttpSession接口

  • getId()返回Session的ID
  • invalidate()使当前的Session失效,Servlet容器会释放HttpSession对象占用的资源
  • setAttribuate(String name, Object value)将一对name/value属性保存在HttpSession对象中
  • getAttribute(String name)根据name参数返回保存在HttpSession对象中的属性值
  • isNew()判断是否是新创建的Session。如果是新创建的Session,返回true,否则返回false
  • setMaxInactiveInterval()设定一个Session可以处于不活动状态的最大时间间隔,以秒为单位,如果超过这个时间,Session会自动失效,如果设置为负数,表示不限制Session处于不活动状态的时间

Session的生命周期

  • 当客户第一次访问Web应用中支持Session的某个网页时,就会开始一个新的Session。接下来当客户浏览这个Web应用得不同网页时,始终处于同一个Session中
  • 默认情况下,JSP网页都是支持Session的,也可以通过显示声明支持Session:<%@ page session="true">

  在以下情况中,Session将结束生命周期,Servlet容器会将Session所占用的资源释放掉

  • 客户端关闭浏览器(当用户第一次访问页面的时候,服务器检查到第一次访问并为其创建一个Session,生成一个Session ID,接着服务器端向客户端发出一个响应,把Session ID作为响应的一部分以Cookie的方式发送给客户端,下一次客户端再次访问这个Web应用时,会把返回来的Session ID这个Cookie信息有发送给服务器端,服务器检查这个Session ID与服务器内存中保存的ID一一匹配,用户处于匹配的那个Session下面。会话Session作为一个Cookie存放在浏览器的进程中,并不存放在硬盘上,浏览器关闭,Session ID就没有了。此外,服务器端并不知道客户端的浏览器关掉了,因此旧的Session依然存在,占据内存,因此默认的Session的有效期限是30分钟)
  • Session过期
  • 服务器端调用了HttpSession的invalidate()方法

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。

  常用的监听接口有以下几个:

  • ServletContextAttributeListener监听对ServletContext属性的操作,比如增加、删除、修改属性
  • ServletContextListener监听ServletContext,当创建ServletContext时,激发contextInitialized(ServletContextEvent sce)方法;当销毁ServletCOntext时,激发contextDestroyed(ServletContextEvent sce)方法

HttpSessionListener监听HttpSession的操作。当创建一个Session时,激发sessionCreated(HttpSessionEvent se)方法;当销毁一个Session时,激发sessionDestroyed(HttpSessionEvent se)方法

  • HttpSessionAttributeListener监听HttpSession中属性的操作。当在Session增加一个属性时,激发attributeAdded(HttpSessionBindingEvent se)方法;当在Session删除一个属性时,激发attributeRemoved(HttpSessionBindingEvent se)方法;当Session属性被重新设置时,激发attributeReplaced(HttpSessionBIndingEvent 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:

getValue

public java.lang.Object getValue()
Gets the value of the ServletContext attribute that changed.

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.

Returns:
the value of the ServletContext attribute that changed

可以看到被替换后的返回值为旧值。

时间: 2024-10-16 05:58:17

第六部分_HttpSession、观察者模式与Listener详解的相关文章

设计模式 - 观察者模式(Observer Pattern) 详解

观察者模式(Observer Pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权所有, 禁止转载, 如有转载, 请站内联系. 观察者模式(Observer Pattern): 定义了对象之间的一对多的依赖, 这样一来, 当一个对象改变状态时, 它的所有依赖者都会收到通知并自动更新. 使用方法: 1. 首先新建主题(subject)接口, 负责注册(register)\删除(remove

“全栈2019”Java第九十六章:抽象局部内部类详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第九十六章:抽象局部内部类详解 下一章 "全栈2019"Java第九十七章:在方法中访问局部内部类成员详解 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学

计算机网络(六),UDP报文段详解

目录 1.UDP作用 2.UDP报文段详解 六.UDP报文段详解 1.UDP作用 (1)面向非连接 (2)不维护连接状态,支持同时向多个客户端传送相同的消息 (3)报文段报头只有8个字节,格外开销较小 (4)吞吐量只受限于数据生成速率.传输速率以及机器性能 (5)尽最大努力交付,不保证可靠交付,不需要维持复杂的链接状态表 (6)面向报文,不对应用程序提交的报文信息进行拆分或者合并 2.UDP报文段详 (1)源端口-2字节 (2)目标端口-2字节 (3)长度-2字节 (4)校验位-2字节 (5)数

Servlet学习笔记(九):监听器Listener详解

一.简介 (一)概述 1.Listener 用于监听 java web程序中的事件,例如创建.修改.删除Session.request.context等,并触发响应的事件. 2. Listener 对应观察者模式,事件发生的时候会自动触发该事件对应的Listeer. Listener 主要用于对 Session.request.context 进行监控.servlet2.5 规范中共有 8 种Listener  . (二)实现 1.不同功能的Listener 需要实现不同的 Listener  

第六章 Django框架学习——ORM详解

第六章 Django框架学习--ORM介绍与常用方法 一.ORM介绍 二.ORM中的常用字段和参数 三.ORM中的关系字段--一对一(OneToOneField) 四.ORM中的关系字段--多对多(ManyToManyField) 五.ORM中的元信息 一.ORM介绍(部分内容前面的章节已经介绍过,此处只做简单介绍) 什么是ORM(what): ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中. Django中的ORM: Django配置数据库 1.在se

【web安全】第六弹:手工SQL注入详解

前一段时间,在对SQL注入有了新的理解之后,写了这篇文章.本来准备投稿,因为内容过于基础被打回来了,想想屯着也没意思,发出来发出来~~本来有好多图的,但是博客园发图很麻烦,word文档的链接会贴在文章最后面,有兴趣的可以下载下来看. 注:本文目标读者是对SQL注入有一定了解,能使用一些工具(SQLMAP.pangolin等)进行自动化SQL注入测试,又想了解工具原理和SQL注入原理的童鞋. 0x00 基础理论篇 0x01 注入技巧&基本模式: 首先,要对下面的一些函数和基本语句有一定的了解. 1

hibernate(六) cascade(级联)和inverse关系详解

序言 写这篇文章之前,自己也查了很多的资料来搞清楚这两者的关系和各自所做的事情,但是百度一搜,大多数博文感觉说的云里雾里,可能博主自己清楚是怎么一回事,但是给一个不懂的人或者一知半解的人看的话,别人也看不懂其中的关系,所以我自己写博文的时候,会尽量用通俗通俗在通俗的语言去描述一个概念,希望能尽自己的力量去帮助你们理解.光看我的是不行的,最关键的是要自己动手去实践一遍,能得出一样的结论,那就说明懂了,在我不懂的时候,我就去自己实现它,一次次尝试,慢慢的就总结出规律了. --WH 一.外键 我为什么

Protobuf详解

按: 新公司前端后端,使用的数据化方式是protobuf (Protocol Buffer). 一,什么是Protobuf 官方文档给出的是: a language-neutral, platform-neutral, extensible way of serializing structured data for use in communications protocols, data storage, and more. 二,Protobuf的优点 1,性能好,效率高 2,代码生成机制,

Bitmap详解与Bitmap的内存优化

感觉这里的排版看着更舒服些 Bitmap详解与Bitmap的内存优化 一.Bitmap: Bitmap是Android系统中的图像处理的最重要类之一.用它可以获取图像文件信息,进行图像剪切.旋转.缩放等操作,并可以指定格式保存图像文件. 常用方法: + public void recycle() // 回收位图占用的内存空间,把位图标记为Dead + public final boolean isRecycled() //判断位图内存是否已释放 + public final int getWid