【Listener】利用监听器Listener以MVC的思想通过JSP+Servlet+JDBC完成在线用户列表的输出

Servlet,监听器Listener与《【Filter】拦截器Filter》(点击打开链接)是JSP的三大核心组件,实际上监听器Listener相当于数据库里面的触发器,一旦用户触发了某种行为,则可以通过相关的Java文件执行相应的程序。用户在浏览网页的过程中,主要有打开浏览器的动作,对应的行为是Session的创建,可是,用户关闭浏览器的动作,并不是对应Session的消失,因此对于Session的消失我们意义不大;访问任意网页的动作,对应的行为是request请求的创建,request的消失对于我们程序猿来说没有任何意义;服务器的自身启动与关闭。对应的行为是Application的创建与消失。

利用监听器Listener配合数据库,可以完成在线用户列表的统计。

一、基本目标

输出一个在线用户列表,设定用户访问我们的网站127.0.0.1:8080/Listener则认为其在线,其实就是localhost:8080/Listener,但localhost:8080,IP地址则变成了0::0:1一个IP6地址非常难看,所以还是使用127.0.0.1:8080,由于无法监听用户是否关闭浏览器,因此设定要是用户5秒内没有访问我们网站的任意一个网页,则认为其已经离线了,只是为了看到实验效果,应该设定得更长。

如下图,开两个浏览器,每一个浏览器对应一个Session,认为是两个用户在访问我们的网站。其实你利用监听器,还可以做得复杂点,通过检查此用户名是否登陆的方式来判断其是否登陆。正如此前我在《【php】基于Xajax的在线聊天室、直播间》(点击打开链接)做过的那样。

二、基本准备

首先在数据库中建立一张在线用户表,如下图:

这张表没有主键,因为需要多次被insert与delete擦写,我也不打算通过主键来统计历史在线人数了,免得主键太难看,所以不设置主键。

由于不设置主键,所以不能通过图形化建表,如果你是通过MySQLQueryBrowser去建表的话,而不是MySQL Command Line Client的话,应该如下图:

在查询语句输入框输入:

create table onlineTable(
  sessionId varchar(45),
  ip varchar(45),
  timeonline LONG
)

建好表之后,在eclipse新建一个网络工程ListenerTest,把上次《【Servlet】根据MVC思想设计用户登陆、用户注册、修改密码系统》(点击打开链接)的Servlet与JDBC的包放到lib,这两个lib网上一搜一把,同时把dbDAO.java放到ListenerTest的src文件夹,并在里面新增一条与插入、修改完全一模一样的删除delete方法,最后整个dbDAO.java如下,几乎就是完全一模一样的,什么都没有改,这就是MVC的优势,由于我们用到同样的一个数据库test,疼一次写好数据库增删改查的类,以后做到多次复用就幸福了。

import java.sql.*;

public class dbDAO {
	private Connection con;

	// 构造函数,连接数据库
	public dbDAO() throws Exception {
		String dburl = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useOldAliasMetadataBehavior=true";
		String dbusername = "root";
		String dbpassword = "root";
		Class.forName("com.mysql.jdbc.Driver");
		this.con = DriverManager.getConnection(dburl, dbusername, dbpassword);
	}

	// 执行查询
	public ResultSet query(String sql, Object... args) throws Exception {
		PreparedStatement ps = con.prepareStatement(sql);
		for (int i = 0; i < args.length; i++) {
			ps.setObject(i + 1, args[i]);
		}
		return ps.executeQuery();
	}

	// 执行插入
	public boolean insert(String sql, Object... args) throws Exception {
		PreparedStatement ps = con.prepareStatement(sql);
		for (int i = 0; i < args.length; i++) {
			ps.setObject(i + 1, args[i]);
		}
		if (ps.executeUpdate() != 1) {
			return false;
		}
		return true;
	}

	// 执行修改
	public boolean modify(String sql, Object... args) throws Exception {
		PreparedStatement ps = con.prepareStatement(sql);
		for (int i = 0; i < args.length; i++) {
			ps.setObject(i + 1, args[i]);
		}
		if (ps.executeUpdate() != 1) {
			return false;
		}
		return true;
	}

	// 执行删除
	public boolean delete(String sql, Object... args) throws Exception {
		PreparedStatement ps = con.prepareStatement(sql);
		for (int i = 0; i < args.length; i++) {
			ps.setObject(i + 1, args[i]);
		}
		if (ps.executeUpdate() != 1) {
			return false;
		}
		return true;
	}

	// 析构函数,中断数据库的连接
	protected void finalize() throws Exception {
		if (!con.isClosed() || con != null) {
			con.close();
		}
	}
}

之后配置好web.xml,这样的片段一般和过滤器一样放置到最顶端,表示整个网站的行为由根目录的onlineListener.java监听,写上这样的监听代码,之后用户一旦触发某种行为,如果在onlineListener.java有相应的代码,则这些代码则会被执行:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
	<listener>
		<listener-class>onlineListener</listener-class>
	</listener>
</web-app>

最后整体的网络结构如下图:

三、制作过程

1、其实就是写好一个Listener.java就OK。与《【Filter】拦截器Filter》(点击打开链接)中一样,一旦我们要监听某一个动作,就必须重写下这个动作的销毁与创建实现方法,因为这里用到了接口,你不写还真的不行。同时也不要怕一个方法太长记不住,Eclipse for JavaEE会帮你自动生成的。可以被监听的方法有ServletRequestListener表示用户访问任意个网址,每访问一个网页则监听/触发一次,实际上就是监听request对象;ServletContextListener服务器的开始与结束监听/触发一次,实际上就是监听Application对象,通过对Application对象的监听可以达到《【Servlet】利用load-on-startup创造一条随服务器共存亡的线程》(点击打开链接)的效果;还有HttpSessionListener,在用户打开浏览器监听/触发一次,实际上监听Session对象的创建与销毁,这里没有用到。

import java.util.*;
import java.sql.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class onlineListener implements ServletRequestListener,
		ServletContextListener {

	// request对象的销毁对我们意义不大
	@Override
	public void requestDestroyed(ServletRequestEvent servletRequestEvent) {

	}

	// request对象的创建相当于,用户访问任意个网页
	@Override
	public void requestInitialized(ServletRequestEvent servletRequestEvent) {
		// 这个方法的参数可以转化成request对象
		HttpServletRequest request = (HttpServletRequest) servletRequestEvent
				.getServletRequest();
		// request对象中取session的方法
		HttpSession session = request.getSession();
		String sessionId = session.getId();
		// request对象中取ip的方法
		String ip = request.getRemoteAddr();
		// 数据库的查询结果
		ResultSet rs = null;
		try {
			// 如果这个sessionID已经在在线用户列表里面的
			// 用户是在线的
			// 那么更新其在线时间
			dbDAO db = new dbDAO();
			rs = db.query("select * from onlinetable where sessionId=?",
					sessionId);
			if (rs.next()) {
				db.modify(
						"update onlinetable set timeonline=? where sessionId=?",
						System.currentTimeMillis(), sessionId);
			}
			// 否则插入在线用户列表
			else {
				db.insert("insert into onlinetable values(?,?,?)", sessionId,
						ip, System.currentTimeMillis());
			}
			// 把当前的在线用户列表放到application里面
			rs = db.query("select * from onlinetable");
		} catch (Exception e) {
			e.printStackTrace();
		}
		// session.getServletContext()相当于application,用application存放在线用户列表
		session.getServletContext().setAttribute("onlineTable", rs);

	}

	// application的消失对我们的意义不大
	// 相当于服务器的关闭,一切都消失了
	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		// TODO Auto-generated method stub

	}

	// application的开始相当于服务器的启动
	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		// TODO Auto-generated method stub
		// 服务器一旦启动每5秒执行如下的任务
		Timer timer = new Timer();
		timer.schedule(new MyTask(), 0, 5000);

	}
}

class MyTask extends TimerTask {
	public void run() {
		try {
			// 对在线用户列表进行检查
			dbDAO db = new dbDAO();
			ResultSet rs = db.query("select * from onlinetable");
			while (rs.next()) {
				// 如果当前时间距离用户上一次在线时间超过5秒
				// 那么则从在线用户列表删除这个结果。
				if (System.currentTimeMillis() - rs.getLong("timeonline") > 5 * 1000) {
					db.delete("delete from onlinetable where sessionId=?",
							rs.getString("sessionId"));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

这里,用到了《【Java】有关System.currentTimeMillis()的思考》(点击打开链接),取出1970年1月1日到现在的毫秒数的概念生成时间戳,与《【Java】利用Timer与TimerTask定时执行任务》(点击打开链接)的概念,每5秒执行一次任务。

2、之后编写一个online.jsp用来显示当前数据库的在线用户列表,因为监听器在每一次监听request请求的过程中,已经把在线用户列表放进application容器里面,并且不断更新里面的消息。application容器是一个所有用户都能看到的,服务器上面的大容器。区别于session容器,是用户每次打开浏览器之后,只是这个浏览器所能够看到的小容器。online.jsp读取这个application容器中的在线用户列表查询结果就可以了。注意取出来的对象,要经过强制类型转换,不转换被报错。

<%@ page language="java" contentType="text/html; charset=utf-8"
	pageEncoding="utf-8"%>
<%@ page import="java.sql.*"%>
<!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>在线用户列表</title>
</head>
<body>
	在线用户列表
	<%ResultSet rs=(ResultSet)application.getAttribute("onlineTable"); %>
	<table border="1">
		<tr>
			<td>ip</td>
			<td>sessionId</td>
		</tr>
		<%
		while(rs.next()){
		%>
		<tr>
			<td><%=rs.getString(2)%></td>
			<td><%=rs.getString(1)%></td>
		</tr>
		<%} %>
	</table>
</body>
</html>

四、总结与展望

其实,整个网络工程的MVC分层如下,MODEL还是之前《【Servlet】根据MVC思想设计用户登陆、用户注册、修改密码系统》(点击打开链接)写好的MODEL,这里由于是同一数据库,完全可以哪里注意。online.jsp作为view,不直接查询数据库,读出C层监听器,放入的查询结果。

虽然利用到《【Java】用JDK1.5之后的新型数组遍历方法遍历HashMap、HashMap不应该存储多元组》(点击打开链接)提到的多元组,同样可以存放在线用户信息,但是之所以使用到数据库存放在线用户信息,是因为可以避免设置一个存放类的ArrayList放入Application容器。存放类的ArrayList放入Application容器不比放入数据库简单,主要是程序不够清晰。

时间: 2024-10-23 08:56:32

【Listener】利用监听器Listener以MVC的思想通过JSP+Servlet+JDBC完成在线用户列表的输出的相关文章

Java监听器Listener使用详解

监听器用于监听web应用中某些对象.信息的创建.销毁.增加,修改,删除等动作的发生,然后作出相应的响应处理.当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法.常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等. 分类:     按监听的对象划分,可以分为 ServletContext对象监听器 HttpSession对象监听器 ServletRequest对象监听器     按监听的事件划分 对象自身的创建和销毁的监听器 对象中属性的创建和消除的监听器

web.xml监听器 - listener

(面向事件编程概念) <listener> <listener-class>com.kiqi.listener.MyListener</listener-class> </listener> 一.监听器类型 1.Listener是Servlet的监听器 2.可以监听客户端的请求.服务端的操作等. 3.通过监听器,可以自动激发一些操作,如监听在线用户数量,当增加一个HttpSession时,给在线人数加1. 4.编写监听器需要实现相应的接口 5.编写完成后在w

Javaweb 工程中 监听器 listener 讲解

监听器用于监听web应用中某些对象.信息的创建.销毁.增加,修改,删除等动作的发生,然后作出相应的响应处理.当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法.常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等. 分类: 按监听的对象划分,可以分为 ServletContext对象监听器 HttpSession对象监听器 ServletRequest对象监听器 按监听的事件划分 对象自身的创建和销毁的监听器 对象中属性的创建和消除的监听器 session中

Javaweb基础---&gt;利用监听器统计在线用户数量和用户信息

首页布局:index.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.*" %> <%@ page import="com.hanqi.model.*" %> <!DOCTYPE

Servlet监听器及在线用户

Servlet中的监听器分为三种类型Ⅰ 监听ServletContext.Request.Session作用域的创建和销毁 (1)ServletContextListener (2)HttpSessionListener (3)ServletRequestListenerⅡ 监听ServletContext.Request.Session作用域中属性变化(增加.修改.删除) (1)ServletContextAttributeListener (2)HttpSessionAttributeLis

JavaWeb-18 (JDBC之分页与监听器listener)

JavaWeb-18 JDBC之分页与监听器listener 一.分页 1.分页概述:分页的结果就是要让指定的记录加载到内存 2.原因: 1.人的习惯 2.内存的限度(核心问题) 3.屏幕的限度 3.如何分页: 不同的环境方式不一样 3.1.数据库层面: select * from acount limit startIndex,size;//只是针对MySql而言(startIndex从0开始,size:共查询多少条记录) select * from acount limit 0,3; 第一页

监听器Listener

一.监听器 监听器是web应用程序事件模型的一部分,当web应用中的某些状态发生改变时,会产生相应的事件,监听器可以接收这些事件,并可以在事件发生时做相应的处理. 二.监听器常用的接口 2.1.javax.servlet.ServletContextListener 实现该接口可以在servlet上下文对象初始化或者销毁时得到通知 2.2.javax.servlet.ServletContextAttributeListener 实现该接口,可以 在servlet上下文的属性列表发生变化时得到通

监听器 (Listener)

1. Listener是Servlet的监听器,它可以监听客户端的请求.服务端的操作等.通过监听器,可以自动激发一些操作,比如监听在线的用户的数量.当增加一个HttpSession时, 就激发sessionCreated(HttpSessionEvent se)方法,这样就可以给在线人数加1 2. 常用的监听接口有以下几个 1). ServletContextAttributeListener监听对ServletContext属性的操作,比如增加.删除.修改属性 2). ServletConte

Servlet监听器Listener

Listener实在servlet2.3中加入的,主要用于对Session,request,context等进行监控. 使用Listener需要实现响应的接口.触发Listener事件的时候,tomcat会自动调用Listener的方法. 在web.xml中配置标签,一般要配置在<servlet>标签前面,可配置多个,同一种类型也可配置多个 <listener> <listener-class>com.xxx.xxx.ImplementListener</list