天天看点

关于servlet监听器的笔记与案例什么是监听器? Servlet中监听器 

什么是监听器? 

监听原理? 事件源(谁被监听了) 监听器(监听事件源的)、监听事件对象(事件源当有一个操作被监听了,就会产生一个事件对象,事件会被传递给监听器)

1、事件源

2、监听器 ----- 为事件源注册监听器

3、监听器中编写监听方法,监听方法参数---- 事件对象

4、事件源执行操作时,产生事件对象,调用监听器监听方法,传递事件对象给监听器

**** 事件对象 有一个行为:获得事件源

Servlet中监听器 

Servlet规范中三种技术:Servlet、Filter、Listener

监听Servlet技术中三种数据范围对象的相关事件:ServletContext、Session、Request

1、创建和销毁的事件监听器

2、属性的增加和删除的事件监听器

3、session域中对象状态

监听器在web.xml中的注册方法

<listenner>
            <listener-class>listenner的实现类</listenner-class>
</listenner>      

第一类 创建和销毁监听器

ServletContextListener 监听全局context对象创建和销毁  ---- 监听创建和销毁方法执行一次

应用场景:有一些程序想随服务器启动而执行

1、创建全局应用数据对象

例如:pageContext.request.contextPath

2、加载框架配置文件

Spring框架org.springframework.web.context.ContextLoaderListener 

3、创建数据库连接池

4、实现任务调度

Timer

TimerTask

定时器案例 :

 编写程序,让程序10点执行 每隔10秒1次(如果想要每天执行即可设置24h一次) ----------- 定时器

定时器编写方法:

package cn.timertest;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 编写定时器
 * 
 * @author seawind
 * 
 */
public class TimerTest {
	public static void main(String[] args) throws ParseException {
		// 编写程序10点运行
		final Timer timer = new Timer(); // 表示定时器
		// 要用schedule方法 编写日程 三个参数:第一个参数 任务、 第二个参数 第一次执行时间、第三个参数 多长时间后重复执行
		// 第一次时间 10点零5分 ,每隔10秒一次、执行5次停止
		String s = "2012-03-07 10:08:00";
		DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		timer.schedule(new TimerTask() {
			int i = 0;

			@Override
			public void run() {
				System.out.println("xxxx");
				i++;
				if (i == 5) {
					// System.exit(0);
					timer.cancel();
				}
			}
		}, dateFormat.parse(s), 10 * 1000);
	}
}
           

HttpSessionListener 监听session的创建和销毁

session何时创建:当客户端访问服务器,服务器端还没有该会话的session对象时,request.getSession() ---- 创建

session何时销毁: 三种 1、服务器停止 2、session.invalidate 3、session过期(session.setMaxInactiveInterval(单位是秒)、配置web.xml(单位分钟 默认30分钟))

案例 : 统计在线人数

原理统计session的数量 ---- session的数量保存在哪?context数据范围

package cn.listener.demo1;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * 统计在线人数
 * 
 * @author seawind
 * 
 */
public class OnlineCountListener implements HttpSessionListener {

	@Override
	public void sessionCreated(HttpSessionEvent se) {
		// 每当方法执行一次,在线人数+1
		// 1、获得当前的在线人数
		ServletContext context = se.getSession().getServletContext();
		int onlineNumber = (Integer) context.getAttribute("onlineNumber");
		onlineNumber++;
		// 保存回context范围
		context.setAttribute("onlineNumber", onlineNumber);
		System.out.println("当前" + onlineNumber + "人在线!");
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		// 每当方法执行一次,在线人数-1
		// 1、获得当前的在线人数
		ServletContext context = se.getSession().getServletContext();
		int onlineNumber = (Integer) context.getAttribute("onlineNumber");
		onlineNumber--;
		// 保存回context范围
		context.setAttribute("onlineNumber", onlineNumber);
		System.out.println("有人离线了...");
		System.out.println("当前" + onlineNumber + "人在线!");
	}

}
           

案例: 定时session销毁扫描器

需求:定制定时器,每隔1分钟执行一次,扫描所有session、如果发现session已经有1分钟没有使用,那么销毁session对象

    先编写TimerContextListener.java监听器

package cn.listener.demo2;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSession;

/**
 * 在服务器启动时,启动一个session 定时扫描器
 * 
 * @author seawind
 * 
 */
public class TimerContextListener implements ServletContextListener {

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		final List<HttpSession> sessionList = new ArrayList<HttpSession>();
		// 将session集合保存到context数据范围
		sce.getServletContext().setAttribute("sessionList", sessionList);

		Timer timer = new Timer();// 定时器
		// 每隔1分钟执行一次
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				// 扫描所有session、如果发现session已经有1分钟没有使用,那么销毁session对象
				// 获得sessionlist 是否需要通过context.getAttribute获得

				synchronized (sessionList) {
					System.out.println("定时销毁程序执行...");
					// 从列表移除session通过迭代器对象
					Iterator<HttpSession> iterator = sessionList.iterator();
					while (iterator.hasNext()) {
						HttpSession session = iterator.next();
						System.out.println("session:"
								+ session.getId()
								+ "未使用时间:"
								+ (System.currentTimeMillis() - session
										.getLastAccessedTime()));
						if (System.currentTimeMillis()
								- session.getLastAccessedTime() >= 1000 * 60) {
							System.out.println("执行" + session.getId()
									+ "的销毁操作...");
							session.invalidate();
							// 从集合移除
							iterator.remove();
						}
					}
				}

				// 下列语句不可行    因为forEach方法不能对子项进行修改删除操作,所以要用迭代器
 				// for (HttpSession session : sessionList) {
				// // 判断session是否一分钟没有使用
				// if (System.currentTimeMillis()
				// - session.getLastAccessedTime() >= 1000 * 60) {
				// session.invalidate();
				// sessionList.remove(session);
				// }
				// }
			}
		}, new Date(), 1000 * 20);
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
	}

}
           

再编写TimerHttpSessionListener.java 监听器

package cn.listener.demo2;

import java.util.List;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class TimerHttpSessionListener implements HttpSessionListener {

	@Override
	// 当有session创建时,保存session到sessionlist
	public void sessionCreated(HttpSessionEvent se) {
		HttpSession session = se.getSession();
		System.out.println("session被创建了,id:" + session.getId());
		// 获得sessionlist
		List<HttpSession> sessionList = (List<HttpSession>) session
				.getServletContext().getAttribute("sessionList");
		synchronized (sessionList) {
			// 添加session到集合
			sessionList.add(session);
		}
		// 这里还需要执行 context的setAttribute吗?
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		System.out.println("session被销毁了,id:" + se.getSession().getId());
	}

}
           

ServletRequestListener 监听请求对象创建和销毁

请求对象生命周期(什么时间创建,什么时间销毁)

客户端发请求时创建、服务器响应结束时销毁

如果执行forward 、include、redirect 哪个会造成 请求对象再次创建?----------redirect

-----------------------------------------------------------------------------------

第二类 属性的增加和删除的事件监听器 

ServletContextAttributeListener HttpSessionAttributeListener ServletRequestAttributeListener

attributeAdded  当数据范围 没有该对象,调用setAttribute 执行

attributeRemoved 当数据范围 执行removeAttribute 调用

attributeReplaced  当调用setAttribute时,该属性范围已经存在相同名称对象,执行

----------------------------------------------------------------------------------

第三类 感知 Session 绑定的事件监听器

 --------------不需要 web.xml 文件中进行注册

session中对象 四种状态: 绑定、解除绑定、钝化、活化

HttpSessionBindingListener 感知当前对象被保存到session和从session移除 ------- 在线用户列表

HttpSessionActivationListener 感知对象被活化和钝化  ---- 容器来完成

活化:将硬盘上对象加载回内存

钝化:将内存中对象保存到硬盘上

主流应用:将session中不常用的对象,保存到硬盘上

如何将对象数据写到硬盘上:序列化

1、编写javabean 实现 serializable  ------- javabean需要序列化

2、javabean实现HttpSessionActivationListener

3、在容器中 配置session要钝化和活化

META-INFO/context.xml

<Context>

<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">

<Store className="org.apache.catalina.session.FileStore" directory="it315"/>

</Manager>

</Context>

4、钝化生成目录 在tomcat/work/工程目录

** j avabean 可序列化: 实现serializable  不然无法实现活化和钝化效果

序列化 作用?

将内存中java对象 ------ 字节流格式

企业应用中:用于将对象数据持久化到硬盘、远程数据传输 (RMI、JavaEE技术、远程访问数据)

案例:显示登陆用户列表,并实现踢人功能。

分析:

显示用户列表,知道哪些用户在线,将在线用户保存集合中(登陆) ---- 用户列表集合(ServletContext)

1、用户列表存放在哪? --- ServletContext

2、什么时候将用户存入列表?---- 登陆成功, 用户信息保存到session (监听器) 

HttpSessionAttributeListener 监听到session中所有属性变化

HttpSessionBindingListener 感知自己被加入session

HttpSessionAttributeListener 和HttpSessionBindingListener 区别?

当session中有任何数据变化,执行HttpSessionAttributeListener 里面三个方法 add remove replace ----- 这个时候变化的数据不一定是用户数据

HttpSessionBindingListener 感知当前对象被加入session, 只有加入session对象是当前数据类型,才会执行valueBound (不用在web.xml中注册)

3、踢人是怎么样实现的?原理:销毁session

想实现踢人,在用户登陆时,保存用户与session 对应关系

Servlet中主要编写 向session添加对象,或从session移除对象操作

对Session列表集合操作 ----- 交给Listener来完成

代码实现:

在index.jsp中实现在线用户列表的显示

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>" target="_blank" rel="external nofollow" >
    
    <title>My JSP 'index.jsp' starting page</title>
	
  </head>
  
  <body>
   	<h1>打印在线用户列表</h1>
   	<c:forEach var="entry" items="${applicationScope.userList}">
   		<h4>${entry.key }<a href="kick?name=${entry.key }" target="_blank" rel="external nofollow" >踢人</a></h4>
   	</c:forEach>
  </body>
</html>
           

编写登陆页面 login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="login" method="post">
	用户名<input type="text" name="username"/>
	密码<input type="password" name="password"/>
	<input type="submit" value="登陆" />
</form>
</body>
</html>           

编写实体类User   

包括id , username,  password  

要实现HttpSessionBindingListener监听器,并实现感知登陆与退出的方法(将user加入userList与移出userList)

package cn.vo;

import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

public class User implements HttpSessionBindingListener {
	private int id;
	private String username;
	private String password;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	// 感知登陆
	public void valueBound(HttpSessionBindingEvent event) {
		// 获得user对象
		User user = (User) event.getValue();
		// 获得列表
		ServletContext context = event.getSession().getServletContext();
		Map<String, HttpSession> userList = (Map<String, HttpSession>) context
				.getAttribute("userList");

		// 将当前登陆用户 加入列表
		userList.put(user.getUsername(), event.getSession());
		System.out.println("添加后map:" + userList);

		// 传引用,无需保存用户列表回context
	}

	@Override
	// 感知退出
	public void valueUnbound(HttpSessionBindingEvent event) {
		// 获得列表
		ServletContext context = event.getSession().getServletContext();
		Map<String, HttpSession> userList = (Map<String, HttpSession>) context
				.getAttribute("userList");
		userList.remove(this.getUsername());
		System.out.println("移除后 map:" + userList);
	}

}
           

编写context监听器

初始化一个用户列表 放入context数据范围

package cn.web.listener;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSession;

public class InitServletContextListener implements ServletContextListener {
	@Override
	public void contextInitialized(ServletContextEvent sce) {
		// 初始化一个用户列表 放入context数据范围
		// Set<String> nameList = new HashSet<String>();
		// sce.getServletContext().setAttribute("userList", nameList);

		// 踢人需要用map实现
		Map<String, HttpSession> userList = new HashMap<String, HttpSession>();
		sce.getServletContext().setAttribute("userList", userList);
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		// TODO Auto-generated method stub

	}

}
           

编写登陆Servlet

package cn.web.servlet;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.itcast.vo.User;

public class LoginServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String username = request.getParameter("username");
		String password = request.getParameter("password");

		boolean loginSuccess = false;
		User user = null;

		// 到数据库查询 是否成功
		String sql = "select * from user where username = ? and password=?";
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		Connection conn = null;
		try {
			conn = DriverManager.getConnection("jdbc:mysql:///day19", "root",
					"123");
			PreparedStatement pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, username);
			pstmt.setString(2, password);
			ResultSet rs = pstmt.executeQuery();
			if (rs.next()) {
				loginSuccess = true;
				user = new User();
				user.setId(rs.getInt("id"));
				user.setUsername(rs.getString("username"));
				user.setPassword(rs.getString("password"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

		if (loginSuccess && user != null) {
			// 登陆成功
			request.getSession().setAttribute("loginUser", user);
			request.getRequestDispatcher("/index.jsp").forward(request,
					response);
		} else {
			// 登陆失败
			request.getRequestDispatcher("/login.jsp").forward(request,
					response);
		}
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}
           

编写踢人Servlet

package cn.web.servlet;

import java.io.IOException;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class KickServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 获得 踢出 用户名称
		String name = request.getParameter("name");
		// 怎么样实现踢人
		Map<String, HttpSession> userList = (Map<String, HttpSession>) getServletContext()
				.getAttribute("userList");
		HttpSession session = userList.get(name); // 这个session就是要踢出session
		session.invalidate();

		request.getRequestDispatcher("/index.jsp").forward(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}
           

即完成显示在线用户与踢人功能