Servlet监听器统计网站在线人数

 
本节我们利用 Servlet 监听器接口,完成一个统计网站在线人数的案例。当一个用户登录后,显示欢迎信息,同时显示出当前在线人数和用户名单。当用户退出登录或 Session 过期时,从在线用户名单中删除该用户,同时将在线人数减 1。

本案例可以通过如下 2 种方案实现:
  1. 使用 HttpSessionListener 和 HttpSessionAttributeListener 实现;
  2. 使用 HttpSessionBindingListener 实现。

使用 HttpSessionListener 和 HttpSessionAttributeListener 实现

1. 创建一个名称为 listennerDemo 的工程,并分别创建 net.biancheng.www.listener 和 net.biancheng.www.servlet 两个包。

2. 在 WebContent 中,创建 login.html,代码如下。
<!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="/listennerDemo/LoginServlet" method="GET">
        <table border="1" width="50%">
            <tr>
                <td colspan="2" align="center">编程帮wwww.biancheng.net</td>
            </tr>
            <tr>
                <td>账号</td>
                <td><input type="text" name="username" /></td>
            </tr>

            <tr>
                <td colspan="2" align="center"><input type="submit" value="提交" />
                </td>
            </tr>
        </table>
    </form>
</body>
</html>

3. 在 net.biancheng.www.servlet  包中,创建名称为 LoginServlet  的 Servlet 类,代码如下。
package net.biancheng.www.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* 登录校验
* @author 编程帮 www.biancheng.net
*/
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @SuppressWarnings("unchecked")
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 设置页面输出格式
        response.setContentType("text/html;charset=UTF-8");
        //修改request缓冲区的字符集为UTF-8
        request.setCharacterEncoding("utf-8");
        PrintWriter writer = response.getWriter();
        // 获取表单数据
        String username = request.getParameter("username");
        // 查看当前会话是否已有账号登录
        String logined = (String) request.getSession().getAttribute("username");

        // 当前会话已有账号登录
        if ("".equals(username) || username == null) {
            System.out.println("非法操作,您没有输入用户名");
            response.sendRedirect("/listennerDemo/login.html");
        } else {
            if (!"".equals(logined) && logined != null) {
                System.out.println("您已经登录,重复登录无效,请先退出当前账号重新登录!");
                writer.write("<h1>编程帮 www.biancheng.net</h1>"
                        + "<h3>您好,您已经登录了账户:" + logined + "</h3>"
                        + "如要登录其他账号,请先退出当前账号重新登录!");
                // 登陆页面为填写内容
            } else {// 将当前账号加入会话中
                request.getSession().setAttribute("username", username);
                writer.write("<h1>编程帮 www.biancheng.net</h1>"
                        + "<h3>" + username + ":   欢迎您的到来</h3>");
            }
            // 从上下文中获取已经登录账号的集合
            List<String> onLineUserList = (List<String>) request.getServletContext().getAttribute("onLineUserList");
            if (onLineUserList != null) {
                // 向页面输出结果
                writer.write(
                        "<h3>   当前在线人数为:" + onLineUserList.size() + "</h3>" + "<table border=\"1\" width=\"50%\">");
                for (int i = 0; i < onLineUserList.size(); i++) {
                    writer.write("<tr>\r\n" + "<td align=\"center\">" + onLineUserList.get(i) + " </td>\r\n" + "</tr>");
                }
            }
            writer.write("</table><br/>" + "<a href=\"/listennerDemo/LogoutServlet\">退出登录</a>");
        }
    }

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

4. 在 net.biancheng.www. listener 包中,创建名称为 MySessionListener 的监听器类,代码如下。
package net.biancheng.www.listener;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
* 监听器
* @author 编程帮 www.bianchengbang.net
*
*/
@WebListener
public class MySessionListener implements HttpSessionListener,HttpSessionAttributeListener{

    @SuppressWarnings("unchecked")
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        System.out.println("HttpSessionAttributeListener****attributeAdded()方法开始工作*******************");
        //从上下文中获取已经登录账号的集合
        List<String> onLineUserList = (List<String>) se.getSession().getServletContext().getAttribute("onLineUserList");
        //在上下文中没有登陆用户
        if(onLineUserList==null || onLineUserList.size()==0) {
            onLineUserList = new ArrayList<String>();               
        }
   
        String username =(String) se.getSession().getAttribute("username");
        //向已登录集合中添加当前账号
        onLineUserList.add(username);
        System.out.println("用户:"+username+" 成功加入在线用户列表");
        for(int i= 0;i<onLineUserList.size();i++) {
            System.out.println(onLineUserList.get(i));
        }
        se.getSession().getServletContext().setAttribute("onLineUserList", onLineUserList);       
    }

    public MySessionListener() {
    }

    @SuppressWarnings("unchecked")
    public void sessionDestroyed(HttpSessionEvent se)  {
        HttpSession session = se.getSession();  
        ServletContext application = session.getServletContext();
        List<String> onlineUserList = (List<String>) application.getAttribute("onLineUserList");
        // 取得登录的用户名
        String username = (String) session.getAttribute("username");
        if(!"".equals(username) && username!=null && onlineUserList!=null && onlineUserList.size()>0 ) {
             // 从在线列表中删除用户名
            onlineUserList.remove(username);
             System.out.println(username+"已经退出!");
             System.out.println("当前在线人数为"+onlineUserList.size());
        }else {
            System.out.println("会话已经销毁!");        
        }    
    }

}

5. 在 net.biancheng.www.servlet 包中,创建名称为 LogoutServlet 的 Servlet 类,代码如下。
package net.biancheng.www.servlet;

import java.io.IOException;
import java.util.List;

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

/**
* 退出登录
* @author 编程帮 www.bianchengbang.net
*
*/
@WebServlet("/LogoutServlet")
public class LogoutServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
 
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //退出登录操作,将此次session进行销毁
        //触发HttpSessionListener监听器的sessionDestroyed方法
        request.getSession().invalidate();
        //跳转回登录页面
       response.sendRedirect("/listennerDemo/login.html");
    }

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

}

6. 启动 Tomcat,在谷歌浏览器地址栏输入“http://localhost:8080/listennerDemo/login.html”,访问 login.html,结果如下图。


7. 输入账号信息后,点击提交,结果如下图。


8. 打开火狐浏览器,访问 login.html,填写账号信息,结果如下图。


 9. 点击提交,结果如下图。


10. 在谷歌浏览器点击退出登录,回到登录首页,切换到火狐浏览器刷新页面,结果如图。

使用 HttpSessionBindingListener 实现

1. 创建一个名称为 sessionBindingDemo 的工程,并分别创建 net.biancheng.www.listener 和 net.biancheng.www.servlet 两个包。

2 .在 WebContent 中,创建 login.html,代码如下。
<!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="/listennerDemo/LoginServlet" method="GET">
        <table border="1" width="50%">
            <tr>
                <td colspan="2" align="center">编程帮wwww.biancheng.net</td>
            </tr>
            <tr>
                <td>账号</td>
                <td><input type="text" name="username" /></td>
            </tr>

            <tr>
                <td colspan="2" align="center"><input type="submit" value="提交" />
                </td>
            </tr>
        </table>
    </form>
</body>
</html>

3. 在 net.biancheng.www.servlet 包中,创建名称为 LoginServlet 的 Servlet 类,代码如下。
package net.biancheng.www.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

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 net.biancheng.www.listener.MyBindingListener;

/**
* 登录
*
* @author 编程帮 www.bianchengbang.net
*/
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @SuppressWarnings("unchecked")
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置页面输出格式
        response.setContentType("text/html;charset=UTF-8");
        request.setCharacterEncoding("utf-8");
        PrintWriter writer = response.getWriter();
        //获取表单数据
        String username = request.getParameter("username");
        // 查看当前会话是否已有账号登录
        MyBindingListener loginedDemo = (MyBindingListener) request.getSession().getAttribute("onlineUserBindingListener");

        //当前会话已有账号登录
        if ("".equals(username) || username == null) {
            System.out.println("非法操作,您没有输入用户名");
            response.sendRedirect("/sessionBindingDemo/login.html");
        } else {
            if (loginedDemo != null) {
                String logined = loginedDemo.getUsername();
                System.out.println("您已经登录,重复登录无效,请先退出当前账号重新登录!");
                writer.write("<h1>编程帮 www.biancheng.net</h1>"
                        + "<h3>您好,您已经登录了账户:" + logined + "</h3>"
                        + "如要登录其他账号,请先退出当前账号重新登录!");
                // 登陆页面为填写内容
            } else {
                //将当前账号加入会话中
                request.getSession().setAttribute("onlineUserBindingListener", new MyBindingListener(username));
                writer.write("<h1>编程帮 www.biancheng.net</h1>"
                        + "<h3>" + username + ":   欢迎您的到来</h3>");
            }
            //向页面输出结果
            //从上下文中获取已经登录账号的集合
            List<String> onLineUserList = (List<String>) request.getServletContext().getAttribute("onlineUserList");
            System.out.println("当前在线用户为:");
            if (onLineUserList != null) {
                for (int i = 0; i < onLineUserList.size(); i++) {
                    System.out.println(onLineUserList.get(i));
                }
            }
            System.out.println("********************************");
            writer.write("<h1>   当前在线人数为:" + onLineUserList.size() + "</h1>"
                    + "<table border=\"1\" width=\"50%\">");
            for (int i = 0; i < onLineUserList.size(); i++) {
                writer.write("<tr>\r\n" +
                        "<td align=\"center\">" + onLineUserList.get(i) + " </td>\r\n" +
                        "</tr>");
            }
            writer.write("</table><br/>"
                    + "<a href=\"/sessionBindingDemo/LogoutServlet\">退出登录</a>");
        }
    }

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

}

4. 在 net.biancheng.www.servlet 包中,创建名称为 LogoutServlet 的 Servlet 类,代码如下。
package net.biancheng.www.servlet;

import java.io.IOException;
import java.util.List;

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

/**
* 退出登录
* @author 编程帮 www.bianchengbang.net
*
*/
@WebServlet("/LogoutServlet")
public class LogoutServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
 
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //退出登录操作,将此次session进行销毁
        //触发HttpSessionListener监听器的sessionDestroyed方法
        request.getSession().invalidate();
        //跳转回登录页面
       response.sendRedirect("/listennerDemo/login.html");
    }

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

}

5. 在 net.biancheng.www. listener 包中创建名称为 MyBindingListener 的监听器类,代码如下。
package net.biancheng.www.listener;

import java.util.ArrayList;
import java.util.List;

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

/**
* 使用MyBindingListener监听
*
* @author 编程帮 www.bianchengbang.net
*/
public class MyBindingListener implements HttpSessionBindingListener {
    private String username;

    public MyBindingListener(String username) {
        super();
        this.username = username;
    }

    public String getUsername() {
        return username;
    }

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

    public MyBindingListener() {
    }

    @SuppressWarnings("unchecked")
    public void valueBound(HttpSessionBindingEvent event) {
        HttpSession session = event.getSession();
        ServletContext application = session.getServletContext();
        // 把用户名放入在线列表
        List<String> onlineUserList = (List) application.getAttribute("onlineUserList");
        // 第一次使用前,需要初始化
        if (onlineUserList == null) {
            onlineUserList = new ArrayList<String>();
        }
        onlineUserList.add(this.username);
        application.setAttribute("onlineUserList", onlineUserList);
        System.out.println("*******用户:" + this.username + "  已经成功加入在线用户列表");
    }


    @SuppressWarnings("unchecked")
    public void valueUnbound(HttpSessionBindingEvent event) {
        HttpSession session = event.getSession();
        ServletContext application = session.getServletContext();
        // 从在线列表中删除用户名
        List<String> onlineUserList = (List<String>) application.getAttribute("onlineUserList");
        onlineUserList.remove(this.getUsername());
        application.setAttribute("onlineUserList", onlineUserList);
        System.out.println(this.username + "退出。");
        System.out.println("当前在线人数:" + onlineUserList.size());
    }

}

6. 启动 Tomcat,在谷歌浏览器地址栏输入“http://localhost:8080/sessionBindingDemo/login.html”,访问 login.html,结果如下图。


7. 输入账号信息后,点击提交,结果如下图。


8. 火狐浏览器,访问 login.html,填写账号信息,结果如下图。


 9. 点击提交,结果如下图。


10. 在谷歌浏览器点击退出登录,切换到火狐浏览器刷新页面,结果如图。