Listener
- 简介
- 常用监听接口
- 监听在线用户信息的实现
- Model层
- Controller层
- OnlineUserListener的实现
- View层
- 测试
简介
监听器(Listener),是一个实现特定接口的普通Java程序,用于监听Web应用中的对象或信息发生改变时,作出相应的响应处理。当被监听对象的状态发生变化时,服务器会自动调用监听器对象中的方法。监听器常常用于系统加载时进行信息初始化、统计在线人数或用户和网站的访问量等。
常用监听接口
接口 | 作用 |
---|---|
ServletContextListener | 监听Web的启动与关闭 |
ServletContextAttributeListener | 监听ServletContext范围内属性的改变 |
ServletRequestListener | 监听用户请求 |
ServletRequestAttributeListener | 监听ServletRequest范围内属性的改变 |
HttpSessionListener | 监听用户session的开始与结束 |
HttpSessionAttributeListener | 监听HttpSession范围内属性的改变 |
监听在线用户信息的实现
首先,创建一个在线用户的表onlineusers,表结构如下图:
Model层
然后,创建一个在线用户表onlineusers与之相映射的主体类OnlineUers,代码如下:
其中,在线用户的属性信息:
sign—主键(session_id)
username—用户名
apage—访问的网页
ip—ip地址
atime—访问的时间(注意:Date类型)
package cn.edu.MVCcase.Model.model;
import java.util.Date;
//在线状态用户类
public class OnlineUers {
private String sign;
private String username;
private String apage;
private String ip;
private Date atime;
public OnlineUers() {
super();
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getApage() {
return apage;
}
public void setApage(String apage) {
this.apage = apage;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Date getAtime() {
return atime;
}
public void setAtime(Date atime) {
this.atime = atime;
}
public OnlineUers(String sign, String username, String apage, String ip, Date atime) {
this.sign = sign;
this.username = username;
this.apage = apage;
this.ip = ip;
this.atime = atime;
}
@Override
public String toString() {
return "OnlineUers{" +
"sign='" + sign + '\'' +
", username='" + username + '\'' +
", apage='" + apage + '\'' +
", ip='" + ip + '\'' +
", atime='" + atime + '\'' +
'}';
}
}
接着,设置实现对数据库onlineusers表的操作的接口OnlineUersDao,代码如下:
package cn.edu.MVCcase.Model.dao;
import cn.edu.MVCcase.Model.model.OnlineUers;
import java.util.List;
public interface OnlineUersDao {
//获取在线用户所有信息
public List<OnlineUers> getOnlineUserInformation();
//新增在线用户信息
public void insertOnlineUserInformation(OnlineUers onlineUers);
//更新在新用户信息
public void updateOnlineUserInformation(OnlineUers onlineUers);
//根据sign删除已下线的用户信息
public void deleteOfflineUserInformation(String sign);
//根据sgin获取在线用户信息
public OnlineUers getOnlineUserInformationBySign(String sign);
}
再实现接口OnlineUersDao的类OnlineUersDaoImpl,同时需要继承Dao的通用基本类CurrencyDao< OnlineUers > (MVC案例的内容),代码如下:
package cn.edu.MVCcase.Model.dao;
import cn.edu.MVCcase.Model.model.OnlineUers;
import java.util.List;
public class OnlineUersDaoImpl extends CurrencyDao<OnlineUers> implements OnlineUersDao {
@Override
public List<OnlineUers> getOnlineUserInformation() {
String sql = "SELECT `sign`,`username`,`apage`,`ip`,`atime` FROM `onlineusers`";
return super.getList(sql);
}
@Override
public void insertOnlineUserInformation(OnlineUers onlineUers) {
String sql = "INSERT `onlineusers` SET `sign`=?,`username`=?,`apage`=?,`ip`=?,`atime`=?";
super.TableOperation(sql,onlineUers.getSign(),onlineUers.getUsername(),onlineUers.getApage(),onlineUers.getIp(),onlineUers.getAtime());
}
@Override
public void updateOnlineUserInformation(OnlineUers onlineUers) {
String sql = "UPDATE `onlineusers` SET `username`=?,`apage`=?,`ip`=?,`atime`=? WHERE `sign`=?";
super.TableOperation(sql,onlineUers.getUsername(),onlineUers.getApage(),onlineUers.getIp(),onlineUers.getAtime(),onlineUers.getSign());
}
@Override
public void deleteOfflineUserInformation(String sign) {
String sql = "DELETE FROM `onlineusers` WHERE `sign`=?";
super.TableOperation(sql,sign);
}
@Override
public OnlineUers getOnlineUserInformationBySign(String sign) {
String sql = "SELECT `sign`,`username`,`apage`,`ip`,`atime` FROM `onlineusers` WHERE `sign`=?";
return super.getSelect(sql,sign);
}
}
同时,为了实现解耦,降低模块间的依赖性,提高程序的独立性,在FactoryDao类中添加如下代码:
public static OnlineUersDao getOnlineUersDao() { return new OnlineUersDaoImpl(); }
再接着,设置对业务处理的接口OnlineUersService,代码如下:
package cn.edu.MVCcase.Model.service;
import cn.edu.MVCcase.Model.model.OnlineUers;
import java.util.List;
public interface OnlineUersService {
//获取在线用户所有信息
public List<OnlineUers> getOnlineUserInformation();
//新增在线用户信息
public void insertOnlineUserInformation(OnlineUers onlineUers);
//更新在新用户信息
public void updateOnlineUserInformation(OnlineUers onlineUers);
//根据sign删除已下线的用户信息
public void deleteOfflineUserInformation(List<OnlineUers> list);
//根据sgin获取在线用户信息
public OnlineUers getOnlineUserInformationBySign(String sign);
}
同理,设置实现接口OnlineUersService的类OnlineUersServiceImpl,代码如下:
package cn.edu.MVCcase.Model.service;
import cn.edu.MVCcase.Model.dao.FactoryDao;
import cn.edu.MVCcase.Model.dao.OnlineUersDao;
import cn.edu.MVCcase.Model.model.OnlineUers;
import java.util.List;
public class OnlineUersServiceImpl implements OnlineUersService {
OnlineUersDao onlineUersDao = FactoryDao.getOnlineUersDao();
@Override
public List<OnlineUers> getOnlineUserInformation() {
return onlineUersDao.getOnlineUserInformation();
}
@Override
public void insertOnlineUserInformation(OnlineUers onlineUers) {
onlineUersDao.insertOnlineUserInformation(onlineUers);
}
@Override
public void updateOnlineUserInformation(OnlineUers onlineUers) {
onlineUersDao.updateOnlineUserInformation(onlineUers);
}
@Override
public void deleteOfflineUserInformation(List<OnlineUers> list) {
//遍历删除
if(list != null && list.size() > 0) {
for (OnlineUers onlineUers:list) {
onlineUersDao.deleteOfflineUserInformation(onlineUers.getSign());
}
}
}
@Override
public OnlineUers getOnlineUserInformationBySign(String sign) {
return onlineUersDao.getOnlineUserInformationBySign(sign);
}
}
同理,在类FactoryService中添加如下代码:
public static OnlineUersService getOnlineUersService() { return new OnlineUersServiceImpl(); }
Controller层
最后,调用service来实现业务,代码如下:
package cn.edu.MVCcase.Controller.controller;
import cn.edu.MVCcase.Model.model.OnlineUers;
import cn.edu.MVCcase.Model.service.FactoryService;
import cn.edu.MVCcase.Model.service.OnlineUersService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
@WebServlet(urlPatterns={"*.onlineuserdo"})
public class OnlineUserController extends HttpServlet {
private static final long serialVersionUID = 1L;
OnlineUersService onlineUersService = FactoryService.getOnlineUersService();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置字符集
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
//获取URL的文件名,如/select.studo
String pathname = req.getServletPath();
//截取select.studo
pathname = pathname.substring(1);
//截取select
pathname = pathname.substring(0,pathname.length()-13);
//获取pathname通过反射调用相应的方法
try {
Method method = this.getClass().getDeclaredMethod(pathname,HttpServletRequest.class,HttpServletResponse.class);
method.invoke(this,req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
public void onlineuser(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List<OnlineUers> list = onlineUersService.getOnlineUserInformation();
req.setAttribute("onlineuser",list);
req.getRequestDispatcher("/OnlineUser.jsp").forward(req,resp);
}
}
OnlineUserListener的实现
监听器实现的基本思路:
首先,记录在线用户信息并写入到onlineuser表中(判断是否存在,如果存在,则更新访问时间与访问页面;如果不存在,则插入该信息);同时,假设在线用户超过十分钟处于静止状态(即没有任何操作),则视为用户已下线,onlineuser表中需要删除该信息(OnlineUserServletContextListener类实现:服务器启动时,每五秒进行检查,发现离线用户则删除该信息);最后,游客登录成功后需要将username改为对应的用户名。
在线用户的监听,信息获取或更新的实现,代码如下:
package cn.edu.MVCcase.OnlineUserListener.listener;
import cn.edu.MVCcase.Model.model.OnlineUers;
import cn.edu.MVCcase.Model.service.FactoryService;
import cn.edu.MVCcase.Model.service.OnlineUersService;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
@WebListener
public class OnlineUserListener implements ServletRequestListener {
OnlineUersService onlineUersService = FactoryService.getOnlineUersService(); //OnlineUersService对象,用于请求操作数据库
@Override
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
}
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
//获取在线用户信息
HttpServletRequest servletRequest = (HttpServletRequest)servletRequestEvent.getServletRequest();
HttpSession session = servletRequest.getSession();
//获取session_id(即sign)
String sign = session.getId();
//获取usekey
String username = (String) session.getAttribute("admin");
username = username==null?"游客":username;
//获取访问的页面
String apage = servletRequest.getRequestURI();
//获取ip地址
String ip = servletRequest.getRemoteAddr();
//操作数据库前先将信息封装
OnlineUers onlineUers = new OnlineUers();
onlineUers.setSign(sign);
onlineUers.setUsername(username);
onlineUers.setApage(apage);
onlineUers.setIp(ip);
onlineUers.setAtime(new Date());
//获取访问时间
OnlineUers onlineUersTableOperation = onlineUersService.getOnlineUserInformationBySign(sign);
if(onlineUersTableOperation != null) {
//更新访问时间与页面
onlineUersTableOperation.setAtime(new Date());
onlineUersTableOperation.setApage(apage);
onlineUersService.updateOnlineUserInformation(onlineUers);
}
else {
//插入信息
onlineUersService.insertOnlineUserInformation(onlineUers);
}
}
}
离线用户的监听,离线用户信息删除与清空的实现,代码如下:
package cn.edu.MVCcase.OnlineUserListener.listener;
import cn.edu.MVCcase.Model.model.OnlineUers;
import cn.edu.MVCcase.Model.service.FactoryService;
import cn.edu.MVCcase.Model.service.OnlineUersService;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@WebListener
public class OnlineUserServletContextListener implements ServletContextListener {
//定义十分钟
public final int MAX_Millis = 10*60*1000;
OnlineUersService onlineUersService = FactoryService.getOnlineUersService();
@Override
//服务器启动时被执行
public void contextInitialized(ServletContextEvent servletContextEvent) {
//视为已下线用户信息
List<OnlineUers> OfflineUsers = new ArrayList<>();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//设置定时器(每隔五秒执行)
new Timer(5*1000, new ActionListener(){
@Override
//检查的具体方法
public void actionPerformed(ActionEvent event) {
//获取在线用户信息
List<OnlineUers> list = onlineUersService.getOnlineUserInformation();
if(list != null && list.size() > 0 ) {
Date OffDate = null;
for (OnlineUers onlineUers:list) {
OffDate = onlineUers.getAtime();
simpleDateFormat.format(OffDate);
Long OffMillis;
try {
OffMillis = simpleDateFormat.parse(OffDate.toString()).getTime();
//当前系统时间,System.currentTimeMillis()
//时间转换成时间戳,Long.parseLong(onlineUers.getAtime().toString())
if ((System.currentTimeMillis()-OffMillis) > MAX_Millis) {
OfflineUsers.add(onlineUers);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
//删除已下线用户的信息
onlineUersService.deleteOfflineUserInformation(OfflineUsers);
//再清空已下线用户信息
OfflineUsers.clear();
}
}).start();
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
用户登录的监听,游客登录成功的username对应修改的实现(注:登录代码中凡成功登录跳转主页面的都需添加),代码如下:
//成功登录,username由游客变成对应的用户名
OnlineUersService onlineUersService = FactoryService.getOnlineUersService();
HttpSession session = req.getSession();
OnlineUers onlineUers = onlineUersService.getOnlineUserInformationBySign(session.getId());
if (onlineUers != null) {
//更换
onlineUers.setUsername(username);
//更新
onlineUersService.updateOnlineUserInformation(onlineUers);
}
View层
现在是重点讲解Listener知识,所以简单做个页面看看,不做太多的修饰,代码如下:
<%@ page import="cn.edu.MVCcase.Model.model.OnlineUers" %>
<%@ page import="java.util.List" %>
<%--
Created by IntelliJ IDEA.
User: dell
Date: 2022/10/15
Time: 12:57
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>在线用户</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" type="text/css" href="CSS/StudentDao.css">
</head>
<body>
<form action="<%=request.getContextPath()%>/onlineuser.onlineuserdo" method="post">
<div id="div_StuDao">
<table id="StuTB">
<tr>
<th>sign</th>
<th>userkey</th>
<th>访问的页面</th>
<th>ip地址</th>
<th>访问的时间</th>
</tr>
<%
List<OnlineUers> list = (List<OnlineUers>) request.getAttribute("onlineuser");
//list有值才进行遍历获取元素
if(list != null && list.size()>0) {
for(OnlineUers onlineUers:list){
%>
<tr>
<td><%=onlineUers.getSign()%></td>
<td><%=onlineUers.getUsername()%></td>
<td><%=onlineUers.getApage()%></td>
<td><%=onlineUers.getIp()%></td>
<td><%=onlineUers.getAtime()%></td>
</tr>
<%
}
}
%>
</table>
</div>
</form>
</body>
</html>
测试
administrator表中有两个账号,测试将两个账号分别在不同的浏览器同时登录,admin在onlineuser.onlineuserdo下观看监听情况,小明在登录主页面。如下图: