好久之前看过下在线人数统计的问题,现在重新整理了一下,把自己写的一些例子发出来和大家分享,希望大家也提出一些想法或者例子,一起研究出最好的方案=^^=
在写文章之前先介绍一个HttpSessionBindingListener这个监听器,用来监听自己什么时候增加到会话中,或者从会话删除。实现HttpSessionBindingListener这个接口要实现2个方法:valueBound(当我知道我在一个会话中时要运行的代码),valueUnbound(当我知道已经不在一个会话时要运行的代码)
代码如下:
//实现一个HttpSessionBindingListener监听器-----OnLineUser.java
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import org.apache.log4j.Logger;
import java.util.*;
public class OnLineUser implements HttpSessionBindingListener {
static Logger logger = Logger.getLogger(OnLineUser.class.getName());
// 创建一个静态变量来保存在线用户
private static Vector<String> users = new Vector<String>();
//返回当前在线人数
public int getCount() {
return users.size();
}
//判断用户是否重复登陆
public boolean existUser(String userName) {
boolean existUser = false; // 用户不存在
for (int i = 0; i < users.size(); i++) {
if (userName.equals((String) users.get(i))) {
existUser = true; // 用户已存在
break;
}
}
return existUser;
}
//从统计中删除用户
public boolean deleteUser(String userName) {
OnLineUser.logger.info("=============移除用户============");
boolean flag = false;
for (int i = 0; i < users.size(); i++) {
if (userName.equals((String) users.get(i))) {
users.remove(i);
flag = true;
break;
}
}
return flag;
}
public void valueBound(HttpSessionBindingEvent e) {
//每当OnLineUser这个实例被绑定到会话里面,就从统计中增加一个在线用户
users.add(e.getName());
OnLineUser.logger.info(e.getName() + "\t登入到系统\t" + (new Date()));
for (int i = 0; i < users.size(); i++) {
OnLineUser.logger.info("第"+ (i+1) + "个在线用户:" + users.get(i));
}
OnLineUser.logger.info("在线用户数为:" + getCount() + "人");
}
public void valueUnbound(HttpSessionBindingEvent e) {
String userName = e.getName();
//每当OnLineUser的实例与Session解除绑定时,就从统计中移除改在线用户
boolean flag = deleteUser(userName);
if (flag == true) {
OnLineUser.logger.info(userName + "\t退出系统\t" + (new Date()));
OnLineUser.logger.info("\t在线用户数为:" + getCount());
}
}
public static Vector getUsers() {
return users;
}
}
//敲完Listener的代码需要在DD中(web.xml)文件配置一下,只需要在web.xml增加下面这段即可
<!-- 配置listener -->
<listener>
<listener-class>com.listener.OnLineUser</listener-class>
</listener>
<!--注意路径不一定是com.listener.OnLineUser,需按照你自己的实际路径 -->
//模拟一个业务逻辑组件的实现-----Service.java
public class Service {
private static Service service;
private String[] user = {"Jam","Rjx","Wjm","Jason"};
private String[] pass = {"123","123","123","123"};
//单例模式的私有构造函数
private Service(){
}
//返回Service实例
public static Service getInstance(){
if(service == null){
synchronized(Service.class){
if(service == null){
service = new Service();
}
}
}
return service;
}
//本来是去数据库检查帐号密码是否一致,这里把情况简单化
public boolean checkUser(String userName,String password){
for(int i=0;i<user.length;i++){
if(userName.equals(user[i]) && password.equals(pass[i])){
return true;
}
}
return false;
}
}
//控制层(表示层)的实现,当用户提交表单时就会来到这个Servlet-----LoginServlet.java
public class LoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request,response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userName = request.getParameter("userName");
String password = request.getParameter("password");
HttpSession session = request.getSession();
//调用service组件的checkUser方法
boolean flag = Service.getInstance().checkUser(userName, password);
if(flag == true){
OnLineUser onLineUser = new OnLineUser();
if(onLineUser.existUser(userName)){
OnLineUser.logger.info(userName+"重复登陆");
}
else{
session.setAttribute(userName, onLineUser);
//设置session的失效时间(秒为单位)
session.setMaxInactiveInterval(10);
}
}
//跳转(请求分派)到onLineResult.jsp页面
request.getRequestDispatcher("onLineResult.jsp").forward(request, response);
}
}
//省略了Servlet在DD中的配置文件
//jsp客户端代码如下,代码比较简单,就不左解释了哈:)-----onLineLogin.jsp
<html>
<head>
<title>统计在线人数登陆页面</title>
</head>
<body>
<form action="LoginServlet" method="post">
<table border="1">
<tr><td colspan="2" align="center"><font color="red">在线人数统计调查表</font></td></tr>
<tr><td>请输入名字:</td><td><input type="text" name="userName"/></td></tr>
<tr><td>请输入密码:</td><td><input type="text" name="password"/></td></tr>
<tr align="center"><td colspan="2"><input type="submit" value="提交"></td></tr>
</table>
</form>
</body>
</html>
//访问完控制层跳转的页面,显示在线人数-----onLineResult.jsp
<html>
<head>
<title>统计在线人数结果</title>
</head>
<body>
现在的在线人数是:<%=OnLineUser.getUsers().size() %>
</body>
</html>
小结:在测试的过程中,我发现就算我设置session的过期时间为1秒,也要等几十秒,才会执行valueUnbound这个方法,不知道是session过期有延迟,还是监听器本身有延迟,暂时没什么头绪。至于这个做法,其中一个缺点是,当在线用户人数多的时候,后台会产生大量的OnLineUser的实例。欢迎大家提出更好的建议和方法:)