Listener使用案例

单态登陆
单态登陆,或者称为单一登陆,就是一个账号只能在一台机器上登陆,如果在其他机器上登陆了,则原来的登陆失效。单态登陆的目的是防止多台机器登陆同一账号。
本例使用一个简单的JSP页面来模拟登陆状况。如果session中有personInfo信息,则表示已经登陆,页面将显示登陆后的账号。如果session中没有personInfo信息,则表示没有登陆,页面将显示登陆输入框。登陆与注销都在JSP页面中完成。
singleton.jsp

<%@page import="logindemo.PersonInfo"%>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%
    String action = request.getParameter("action");
    String account = request.getParameter("account");

    if("login".equals(action) && account.trim().length()>0){
        //如果为登陆动作
        PersonInfo personInfo = new PersonInfo();
        personInfo.setAccount(account.trim().toLowerCase());
        personInfo.setIp(request.getRemoteAddr());
        personInfo.setLoginDate(new  Date());

        session.setAttribute("personInfo", personInfo);

        response.sendRedirect(response.encodeRedirectUrl(request.getRequestURI()));
        return;
    }else if("logout".equals(action)){
        session.removeAttribute("personInfo");
        response.sendRedirect(response.encodeRedirectUrl(request.getRequestURI()));
        return;
    }
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTMLper 4.01 Transitional//EN">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert title here</title>
        <style type="text/css">
        body {
            font-size:12px; 
        }
        </style>
  </head>

  <body>
    <c:choose>
            <c:when test="${ personInfo != null }">
                <!-- 已经登录,将显示帐号信息 -->
                欢迎您,${ personInfo.account }。<br/> 
                您的登录IP为${ personInfo.ip },<br/>
                登录时间为<fmt:formatDate value="${ personInfo.loginDate }" pattern="yyyy-MM-dd HH:mm"/><a href="${ pageContext.request.requestURI }?action=logout">退出</a>
                <!-- 每5秒钟刷新一次页面 -->
                <script>setTimeout("location=location; ", 5000); </script>
            </c:when>
            <c:otherwise>
                <!-- 没有登录,将显示登录页面 -->
                ${ msg } 
                <c:remove var="msg" scope="session" />
                <form action="${ pageContext.request.requestURI }?action=login" method="post">
                    帐号:
                    <input name="account" />
                    <input type="submit" value="登录">
                </form>
            </c:otherwise>
        </c:choose>
  </body>
</html>

为了信息的实时性,登陆后的代码中添加了js代码,每隔5秒刷新一次页面。这样,如果账号在别处登陆,5秒之内就会强制下线。
PersonInfo.java

package logindemo;

import java.io.Serializable;
import java.util.Date;

public class PersonInfo implements Serializable {

    private static final long serialVersionUID = 4063123342511223123L;

    private String account;
    private String ip;
    private Date loginDate;

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public Date getLoginDate() {
        return loginDate;
    }

    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null || !(obj instanceof PersonInfo)) {
            return false;
        }
        return account.equals(((PersonInfo) obj).getAccount());
    }
}

JSP代码中没有任何实现单态登陆的代码。单态登陆的代码写在listener中,通过监听Session的personInfo属性的添加删除实现的。如果新添加了一个personInfo属性,则认为是新登录用户,Listener中查看该用户账号是否已经在其他机器上登陆,如果已经登陆过了,则把旧的登陆信息失效。
该Listener可能需要修改其他用户的session,而正常情况下其他用户的session是对当前登陆用户不可见的。因此Listener中使用一个Map把所有包含personInfo信息的session收集起来,以便索引或修改相关session。

LoginSessionListener.java

package logindemo;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

public class LoginSessionListener implements HttpSessionAttributeListener {

    Map<String, HttpSession> map = new HashMap<String, HttpSession>();

    public void attributeAdded(HttpSessionBindingEvent event) {
        String name = event.getName();
        // 登录
        if (name.equals("personInfo")) {
            PersonInfo personInfo = (PersonInfo) event.getValue();
            if (map.get(personInfo.getAccount()) != null) {
                // map 中有记录,表明该帐号在其他机器上登录过,将以前的登录失效
                HttpSession session = map.get(personInfo.getAccount());
                PersonInfo oldPersonInfo = (PersonInfo) session
                        .getAttribute("personInfo");

                session.removeAttribute("personInfo");
                session.setAttribute("msg", "您的帐号已经在其他机器上登录,您被迫下线。");
            }

            // 将session以用户名为索引,放入map中
            map.put(personInfo.getAccount(), event.getSession());
            // System.out.println("添加用户成功,现在登陆人数" + map.size());
        }
    }

    public void attributeRemoved(HttpSessionBindingEvent event) {
        String name = event.getName();
        // 注销
        if (name.equals("personInfo")) {
            // 将该session从map中移除
            PersonInfo personInfo = (PersonInfo) event.getValue();
            map.remove(personInfo.getAccount());
        }
        // System.out.println("删除用户成功,现在登陆人数" + map.size());
    }

    public void attributeReplaced(HttpSessionBindingEvent event) {
        String name = event.getName();
        // 没有注销的情况下,用另一个帐号登录
        if (name.equals("personInfo")) {
            // 移除旧的的登录信息
            PersonInfo oldPersonInfo = (PersonInfo) event.getValue();
            map.remove(oldPersonInfo.getAccount());
            // 新的登录信息
            PersonInfo personInfo = (PersonInfo) event.getSession()
                    .getAttribute("personInfo");
            // 也要检查新登录的帐号是否在别的机器上登录过
            if (map.get(personInfo.getAccount()) != null) {
                // map 中有记录,表明该帐号在其他机器上登录过,将以前的登录失效
                HttpSession session = map.get(personInfo.getAccount());
                session.removeAttribute("personInfo");
                session.setAttribute("msg", "您的帐号已经在其他机器上登录,您被迫下线。");
            }
            map.put("personInfo", event.getSession());
        }
        // System.out.println("修改用户成功,现在登陆人数" + map.size());
    }
}

web.xml

<listener>
        <listener-class>logindemo.LoginSessionListener</listener-class>
    </listener>

这里写图片描述
这里写图片描述

显示在线用户

本例将使用L
istener记录服务器的信息,包括启动时间、累计访问人数、最大同时在线数以及发生的时间、当前用户数以及当前登陆用户数等,并列出所有的在线用户信息,包括账号、第一次访问时间、最后一次访问时间、访问次数及ip地址等。运行效果见下图。

这里写图片描述

为了简单起见,将所有的数据放到ApplicationConstants类的静态属性中。
ApplicationConstants.java

package serverinfodemo;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpSession;

public class ApplicationConstants {

    // 所有的 Session
    public static Map<String, HttpSession> SESSION_MAP = new HashMap<String, HttpSession>();

    // 当前登录的用户总数
    public static int CURRENT_LOGIN_COUNT = 0;

    // 历史访客总数
    public static int TOTAL_HISTORY_COUNT = 0;

    // 服务器启动时间
    public static Date START_DATE = new Date();

    // 最高在线时间
    public static Date MAX_ONLINE_COUNT_DATE = new Date();

    // 最高在线人数
    public static int MAX_ONLINE_COUNT = 0;
}

该例中使用4中listener:ServletContextListener、HttpSessionListener、HttpSessionAttributeListener以及ServletRequestListener。

使用ServletContextListener来监听服务器的启动与关闭,记录服务器的启动时间等。
MyContextListener.java

package serverinfodemo;

import java.util.Date;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyContextListener implements ServletContextListener {

    public void contextDestroyed(ServletContextEvent arg0) {
        ApplicationConstants.START_DATE = null;
        ApplicationConstants.MAX_ONLINE_COUNT_DATE = null;
    }

    public void contextInitialized(ServletContextEvent arg0) {
        ApplicationConstants.START_DATE = new Date();
    }

}

对session的监听比较复杂,需要维护在线用户列表、总访问人数等。为简单起见,本例仍使用Map来索引所有session。session创建的时候,将session放入map中;session销毁的时候,从map中将session移除,以保证所有在线用户的session都在map中。当session的personInfo属性发生变化时,维护用户的登陆与注销。
MySessionListener.java

package serverinfodemo;

import java.util.Date;

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;

public class MySessionListener implements HttpSessionListener,
        HttpSessionAttributeListener {

    /**
     * 添加属性时调用
     */
    public void attributeAdded(HttpSessionBindingEvent se) {
        if (se.getName().equals("personInfo")) {
            ApplicationConstants.CURRENT_LOGIN_COUNT++; // 当前登陆用户++
            HttpSession session = se.getSession();

            // 查找该账号有没有在其他机器上登陆
            for (HttpSession sess : ApplicationConstants.SESSION_MAP.values()) {
                // 如果该账号已经在其他机器上登陆,则以前的登陆失败
                if (se.getValue().equals(sess.getAttribute("personInfo"))
                        && session.getId() != sess.getId()) {
                    sess.invalidate();
                    ApplicationConstants.CURRENT_LOGIN_COUNT--;
                }
            }
        }
    }

    /**
     * 移除属性时调用
     */
    public void attributeRemoved(HttpSessionBindingEvent se) {
        if (se.getName().equals("personInfo")) {
            ApplicationConstants.CURRENT_LOGIN_COUNT--; // 当前登陆用户--
        }
    }

    public void attributeReplaced(HttpSessionBindingEvent se) {
        if (se.getName().equals("personInfo")) { // 重新登陆
            HttpSession session = se.getSession();
            // 查找该账号有没有在其他机器上登陆
            for (HttpSession sess : ApplicationConstants.SESSION_MAP.values()) {
                // 如果该账号已经在其他机器上登陆,则以前的登陆失败
                if (se.getValue().equals(sess.getAttribute("personInfo"))
                        && session.getId() != sess.getId()) {
                    sess.invalidate();
                    ApplicationConstants.CURRENT_LOGIN_COUNT--;
                }
            }
        }
    }

    /**
     * session创建时被调用
     */
    public void sessionCreated(HttpSessionEvent arg0) {
        HttpSession session = (HttpSession) arg0.getSession();
        ApplicationConstants.SESSION_MAP.put(session.getId(), session);

        ApplicationConstants.TOTAL_HISTORY_COUNT++; // 总访问人数++
        if (ApplicationConstants.SESSION_MAP.size() > ApplicationConstants.MAX_ONLINE_COUNT) {
            ApplicationConstants.MAX_ONLINE_COUNT = ApplicationConstants.SESSION_MAP
                    .size();
            ApplicationConstants.MAX_ONLINE_COUNT_DATE = new Date();
        }
    }

    /**
     * session销毁时调用
     */
    public void sessionDestroyed(HttpSessionEvent arg0) {
        HttpSession session = (HttpSession) arg0.getSession();
        ApplicationConstants.SESSION_MAP.remove(session.getId());
    }

}

监听request主要是记录客户的ip地址、访问次数等,也可以记录用户访问的URI。
MyRequestListener.java

package serverinfodemo;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class MyRequestListener implements ServletRequestListener {

    public void requestDestroyed(ServletRequestEvent sre) {

    }

    public void requestInitialized(ServletRequestEvent sre) {
        HttpServletRequest request = (HttpServletRequest) sre
                .getServletRequest();
        HttpSession session = request.getSession(true);
        session.setAttribute("ip", request.getRemoteAddr()); // 记录ip地址

        String uri = request.getRequestURI(); // 访问的URI
        String[] suffix = { ".html", ".do", ".jsp", ".action" }; // 指定后缀
        for (int i = 0; i < suffix.length; i++) {
            if (uri.endsWith(suffix[i])) {
                // 如果是指定后缀,程序继续运行
                break;
            }
            if (i == suffix.length - 1) {
                // 否则返回
                return;
            }
        }
        Integer activeTimes = (Integer) session.getAttribute("activeTimes"); // 获取访问次数
        if (activeTimes == null) {
            activeTimes = 0;
        }
        session.setAttribute("activeTimes", activeTimes + 1); // 更新访问次数
    }

}

web.xml

<listener>
        <listener-class>serverinfodemo.MyContextListener</listener-class>
    </listener>
    <listener>
        <listener-class>serverinfodemo.MySessionListener</listener-class>
    </listener>
    <listener>
        <listener-class>serverinfodemo.MyRequestListener</listener-class>
    </listener>

online.jsp

<%@page import="java.text.SimpleDateFormat"%>
<%@page import="logindemo.PersonInfo"%>
<%@page import="serverinfodemo.ApplicationConstants"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<jsp:directive.page import="java.util.Date" />
<jsp:directive.page import="java.text.DateFormat" />
<style>
    body, td {font-size: 12px; }
</style>

服务器启动时间:<%=new SimpleDateFormat("yyyy-MM-dd HH:mm").format(ApplicationConstants.START_DATE)%>,
累计共接待过 <%= ApplicationConstants.TOTAL_HISTORY_COUNT %> 访客。<br/>
同时在线人数最多为 <%= ApplicationConstants.MAX_ONLINE_COUNT %> 人,
发生在 <%=DateFormat.getDateTimeInstance().format(ApplicationConstants.MAX_ONLINE_COUNT_DATE)%><br/>

目前在线总数:<%= ApplicationConstants.SESSION_MAP.size() %>,登录用户:<%=ApplicationConstants.CURRENT_LOGIN_COUNT%><br/>
<table border=1>
    <tr>
        <th>jsessionid</th>
        <th>account</th>
        <th>creationTime</th>
        <th>lastAccessedTime</th>
        <th>new</th>
        <th>activeTimes</th>
        <th>ip</th>
    </tr>
    <%
        for (String id : ApplicationConstants.SESSION_MAP.keySet()) {
            HttpSession sess = ApplicationConstants.SESSION_MAP.get(id);
            PersonInfo personInfo = (PersonInfo)sess.getAttribute("personInfo");
    %>
    <tr <%= session == sess ? "bgcolor=#DDDDDD" : "" %>>
        <td><%=id%></td>
        <td><%=personInfo==null ? "&nbsp;" : personInfo.getAccount()%></td>
        <td><%=DateFormat.getDateTimeInstance().format(sess.getCreationTime())%></td>
        <td><%=DateFormat.getDateTimeInstance().format(
                                new Date(sess.getLastAccessedTime()))%></td>
        <td><%=sess.isNew()%></td>
        <td><%=sess.getAttribute("activeTimes")%></td>
        <td><%=sess.getAttribute("ip") %></td>
    </tr>
    <%
    }
    %>
</table>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值