1. 监听器
1.1 概述
监听器: 主要是用来监听特定对象的创建或销毁、属性的变化的!
是一个实现特定接口的普通java类!
对象:
自己创建自己用 (不用监听)
别人创建自己用 (需要监听)
Servlet中哪些对象需要监听?
request / session / servletContext
分别对应的是request监听器、session相关监听器、servletContext监听器
监听器(listener)
监听器接口:
一、监听对象创建/销毁的监听器接口
Interface ServletRequestListener 监听request对象的创建或销毁
Interface HttpSessionListener 监听session对象的创建或销毁
Interface ServletContextListener 监听servletContext对象的创建或销毁
二、监听对象属性的变化
Interface ServletRequestAttributeListener 监听request对象属性变化:添加、移除、修改
Interface HttpSessionAttributeListener 监听session对象属性变化:添加、移除、修改
Interface ServletContextAttributeListener 监听servletContext对象属性变化
三、session相关监听器
Interface HttpSessionBindingListener 监听对象绑定到session上的事件
Interface HttpSessionActivationListener(了解)监听session序列化及反序列化的事件
1.2 生命周期监听器
声明周期监听器: 监听对象的创建、销毁的过程!
监听器开发步骤:
1. 写一个普通java类,实现相关接口;
2. 配置(web.xml)
A. ServletRequestListener
监听request对象的创建或销毁。
代码:
/**
* 监听request对象的创建或销毁
* @author Jie.Yuan
*
*/
public class MyRequestListener implements ServletRequestListener{
// 对象销毁
@Override
public void requestDestroyed(ServletRequestEvent sre) {
// 获取request中存放的数据
Object obj = sre.getServletRequest().getAttribute("cn");
System.out.println(obj);
System.out.println("MyRequestListener.requestDestroyed()");
}
// 对象创建
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("MyRequestListener.requestInitialized()");
}
}
Web.xml
<!-- 监听request对象创建、销毁 -->
<listener>
<listener-class>cn.itcast.a_life.MyRequestListener</listener-class>
</listener>
B. HttpSessionListener
监听session对象的创建或销毁。
C. ServletContextListener
监听servletContext对象的创建或销毁。
1.3 属性监听器
监听:request/session/servletContext对象属性的变化!
ServletRequestAttributeListener
HttpSessionAttributeListener
ServletContextAttributeListener
总结:先写类,实现接口; 再配置
1.4 其他监听器: session相关监听器
HttpSessionBindingListener
监听对象绑定/解除绑定到sesison上的事件!
步骤:
对象实现接口; 再把对象绑定/解除绑定到session上就会触发监听代码。
作用:
(例如上线提醒!)
/**
* 监听此对象绑定到session上的过程,需要实现session特定接口
* @author Jie.Yuan
*
*/
public class Admin implements HttpSessionBindingListener {
… 省略get/set
// 对象放入session
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("Admin对象已经放入session");
}
// 对象从session中移除
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("Admin对象从session中移除!");
}
}
思考:
这个session监听器,和上面的声明周期、属性监听器区别?
--à 不用再web.xml配置
-à 因为监听的对象是自己创建的对象,不是服务器对象!
1.5 案例
需求:做一个在线列表提醒的功能!
用户--à 登陆
---à 显示登陆信息,列表展示。(list.jsp)
--à 显示在线用户列表 (list.jsp)
-à 列表点击进入“在线列表页面” onlineuser.jsp
实现:
1. 先增加登陆、退出功能;;
login.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<form name="frmLogin" action="${pageContext.request.contextPath }/admin?method=login" method="post">
<table align="center" border="1">
<tr>
<td>用户名</td>
<td>
<input type="text" name="userName">
</td>
</tr>
<tr>
<td>密码</td>
<td>
<input type="password" name="pwd">
</td>
</tr>
<tr>
<td>
<input type="submit" value="亲,点我登陆!">
</td>
</tr>
</table>
</form>
</body>
</html>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 登陆验证过滤器 -->
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>cn.itcast.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 监听器: 监听ServletContext对象 -->
<listener>
<listener-class>cn.itcast.listener.OnlineAdminListener</listener-class>
</listener>
<!-- 监听器:监听session对象 -->
<listener>
<listener-class>cn.itcast.listener.SessionListener</listener-class>
</listener>
<servlet>
<servlet-name>IndexServlet</servlet-name>
<servlet-class>cn.itcast.servlet.IndexServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>AdminServlet</servlet-name>
<servlet-class>cn.itcast.servlet.AdminServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AdminServlet</servlet-name>
<url-pattern>/admin</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>IndexServlet</servlet-name>
<url-pattern>/index</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
2. 写监听器,监听servletContext对象的创建: 初始化集合(onlineuserlist)
package cn.itcast.listener;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import cn.itcast.entity.Admin;
/**
* 初始化在线列表集合监听器
*
* @author Jie.Yuan
*
*/
public class OnlineAdminListener implements ServletContextListener {
//1. ServletContext对象创建
@Override
public void contextInitialized(ServletContextEvent sce) {
// 创建集合:存放在线用户
// 每次当用户登陆后,就往这个集合中添加添加当前登陆者
List<Admin> onlineList = new ArrayList<Admin>();
// 放入ServletContext中
sce.getServletContext().setAttribute("onlineList", onlineList);
}
//2. ServletContext对象销毁
@Override
public void contextDestroyed(ServletContextEvent sce) {
// 获取ServletContext
ServletContext sc = sce.getServletContext();
// 获取在线列表
Object obj = sc.getAttribute("onlineList");
// 移除在线列表集合
if (obj != null) {
sc.removeAttribute("onlineList");
}
}
}
3. 登陆功能: 用户登陆时候,把数据保存到servletContext中
package cn.itcast.servlet;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import cn.itcast.entity.Admin;
import cn.itcast.service.IAdminService;
import cn.itcast.service.impl.AdminService;
/**
* 用户管理servlet 1. 登陆 2. 退出
*
* @author Jie.Yuan
*
*/
public class AdminServlet extends HttpServlet {
// Service实例
private IAdminService adminService = new AdminService();
// 跳转资源
private String uri;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 区别不同的操作类型
String method = request.getParameter("method");
// 判断
if ("login".equals(method)) {
// 登陆操作, 调用登陆方法
login(request, response);
}
else if ("out".equals(method)) {
// 退出操作,调用推出方法
out(request, response);
}
}
// 1 登陆
private void login(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// 1. 获取参数
String userName = request.getParameter("userName");
String pwd = request.getParameter("pwd");
// 封装
Admin admin = new Admin();
admin.setUserName(userName);
admin.setPwd(pwd);
try {
// 2. 调用service处理业务
Admin loginInfo = adminService.findByNameAndPwd(admin);
// 判断:
if (loginInfo == null) {
// 登陆失败
uri = "/login.jsp";
} else {
// 登陆成功
// 先,保存数据到session
request.getSession().setAttribute("loginInfo", loginInfo);
//【在线列表: 1. 先从servletContext中拿到在线列表集合; (onLineUserList)
// 2. 当前用户放入“在线列表集合中”】
// 实现1:先得到servletContext对象
ServletContext sc = getServletContext();
// 实现2: 再获取在线列表集合
List<Admin> onlineList = (List<Admin>) sc.getAttribute("onlineList");
// 判断
if (onlineList != null){
// 实现3: 添加当前登陆者
onlineList.add(loginInfo);
//sc.setAttribute("onlineList", onlineList); // 对象引用传递,不需要写也可以
}
// 再,跳转到首页显示servlet(/index)
uri = "/index";
}
} catch (Exception e) {
// 测试
e.printStackTrace();
// 错误
uri = "/error/error.jsp";
}
// 3. 跳转
request.getRequestDispatcher(uri).forward(request, response);
}
// 2 退出
private void out(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1. 获取session
HttpSession session = request.getSession(false);
//2.判断:
if (session!=null) {
// 从session中移除用户
// session.removeAttribute("loginInfo"); //?
// 销毁session
session.invalidate();
}
//3. 跳转(登陆)
response.sendRedirect(request.getContextPath() + "/login.jsp");
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doGet(req, resp);
}
}
4. List.jsp 增加超链接, 点击时候提交直接跳转到online.jsp
list.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!-- 引入jstl核心标签库 -->
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<!-- 引入头部页面 -->
<jsp:include page="/public/head.jsp"></jsp:include>
<!-- 列表展示数据 -->
<table align="center" border="1" width="80%" cellpadding="3" cellspacing="0">
<tr>
<td>序号</td>
<td>编号</td>
<td>员工名称</td>
</tr>
<c:if test="${not empty requestScope.listEmp}">
<c:forEach var="emp" items="${requestScope.listEmp}" varStatus="vs">
<tr>
<td>${vs.count }</td>
<td>${emp.empId }</td>
<td>${emp.empName }</td>
</tr>
</c:forEach>
</c:if>
</table>
</body>
</html>
online.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!-- 引入jstl核心标签库 -->
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>在线用户列表</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<!-- 引入头部页面 -->
<jsp:include page="/public/head.jsp"></jsp:include>
<!-- 在线用户 -->
<table align="center" border="1" width="80%" cellpadding="3" cellspacing="0">
<tr>
<td colspan="2" align="center"><h3>在线列表展示:</h3></td>
</tr>
<tr>
<td>编号</td>
<td>员工名称</td>
</tr>
<c:if test="${not empty applicationScope.onlineList}">
<c:forEach var="admin" items="${applicationScope.onlineList}">
<tr>
<td>${admin.id }</td>
<td>${admin.userName }</td>
</tr>
</c:forEach>
</c:if>
</table>
</body>
</html>
head.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<h3 align="center">
欢迎你,${sessionScope.loginInfo.userName };
<a href="${pageContext.request.contextPath }/admin?method=out">退出</a>
<a href="${pageContext.request.contextPath }/online.jsp">在线列表展示</a>
</h3>
5. 写监听器: 监听session销毁,把当前登陆用户从onlineuserlist移除!
package cn.itcast.listener;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import cn.itcast.entity.Admin;
/**
* 监听Session销毁的动作:
* 当服务器销毁session的时候,从在线列表集合中移除当亲的登陆用户
* @author Jie.Yuan
*
*/
public class SessionListener implements HttpSessionListener{
@Override
public void sessionDestroyed(HttpSessionEvent se) {
//1. 获取Session对象、ServletContext对象
HttpSession session = se.getSession();
ServletContext sc = session.getServletContext();
//2. 获取Session中存储的当前登陆用户
Object obj = session.getAttribute("loginInfo");//?
//3. 获取ServletContext中存储的在线用户列表集合
List<Admin> list = (List<Admin>) sc.getAttribute("onlineList");
// 先判断
if (obj != null){
//4. 把“当前登陆用户”从在线列表集合中移除
list.remove(obj);
}
}
@Override
public void sessionCreated(HttpSessionEvent se) {
}
}