Servlet笔记(9):监听器Listener详解

简介

概述

  1. Listener 用于监听 java web 程序中的事件,例如创建、修改、删除Session、request、context等,并触发响应的事件
  2. Listener 对应观察者模式,事件发生的时候会自动触发该事件对应的Listeer。 Listener 主要用于对 Session、request、context 进行监控。servlet2.5 规范中共有 8 种Listener

自定义实现

实现接口
  1. 不同功能的 Listener 需要实现不同的 Listener 接口,一个Listener也可以实现多个接口,这样就可以多种功能的监听器一起工作
  2. 8种监听器可以分为三类:
  • 监听 Session、request、context 的创建与销毁,分别为 HttpSessionLister、ServletContextListener、ServletRequestListener
  • 监听对象属性变化,分别为:HttpSessionAttributeLister、ServletContextAttributeListener、ServletRequestAttributeListener
  • 监听Session 内的对象, 分别为HttpSessionBindingListenerHttpSessionActivationListener。与上面两类类不同,这两个 Listener 监听的是Session 内的对象,而非 Session 本身,不需要在 web.xml 中配置
配置
  1. listenerlistener-class 标签
  2. listener一般配置在 servlet 标签的前面
自定义实现如下
  1. 代码
public class CustomListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("监听创建");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("监听销毁");
    }
}

  1. 配置
<!--自定义监听器 -->
<listener>
    <listener-class>com.learning.servlet2x.listener.CustomListener</listener-class>
</listener>

监听器详解(8个)

监听 Session、request、context 的创建和销毁

  1. ServletContextListener: 监听 Context 的创建和销毁,可用于启动时获取 web.xml里配置的初始化参数、获取字典表数据等

  2. HttpSessionLister: 监听 Session 的创建与销毁。可以统计session的个数,从而记录在线者信息

  3. ServletRequestListener: 监听 Request 的创建与销毁。用户每次请求request都会执行该监听器的requestInitialized方法。

  4. 测试

  • 自定义一个监听器,分别实现以上三个接口, 具体代码底部源码链接
  • public class ObjectListener implements HttpSessionListener, ServletContextListener, ServletRequestListener

监听对象属性变化

  1. ServletContextAttributeListener:监听 Context 中属性的变化,如添加、更新、删除

  2. HttpSessionAttributeLister:监听 Session 中属性的变化,如添加、更新、删除

  3. ServletRequestAttributeListener:监听 Request 中属性的变化,如添加、更新、删除

  4. 测试

  • 自定义一个监听器,分别实现以上三个接口, 具体代码底部源码链接
  • public class ObjectAttributeListener implements ServletContextAttributeListener, HttpSessionAttributeListener, ServletRequestAttributeListener

监听 Session 内的对象(不需要web的配置、对象必须实现Listener接口)

  1. HttpSessionBindingListener:对象被放入、取出 Session 被触发
  2. HttpSessionActivationListener:Session里面的内容保存到硬盘,或者从硬盘读取
  3. 测试
  • 自定义一个监听器,分别实现以上2个接口, 具体代码底部源码链接
  • public class SessionObjectListener implements HttpSessionActivationListener, HttpSessionBindingListener, Serializable

常用监听器

单态登录:一个账号只能在一台机器上登录:HttpSessionAttributeListener

public class LoginSessionListener implements HttpSessionAttributeListener {


    /**
     * 保存 Session
     */
    Map<String, HttpSession> map = new HashMap<String, HttpSession>();

    /**
     * Session 属性添加
     *
     * @param event
     */
    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {

        // 被添加进来的属性名称
        String name = event.getName();

        // 登录
        if (name.equals(PersonInfo.SESSION_PERSON)) {

            PersonInfo personInfo = (PersonInfo) event.getValue();

            // map 中有记录,表明该帐号在其他机器上登录过,将以前的登录失效
            HttpSession session = map.get(personInfo.getAccount());
            if (session != null) {
                //map 已经存在的旧的信息
                PersonInfo oldPersonInfo = (PersonInfo) session.getAttribute(PersonInfo.SESSION_PERSON);
                System.out.println("帐号" + oldPersonInfo.getAccount() + "在" + oldPersonInfo.getIp() + "已经登录,该登录将被迫下线。");
                session.removeAttribute(PersonInfo.SESSION_PERSON);
                session.setAttribute(PersonInfo.SESSION_MSG, "您的帐号已经在其他机器上登录,您被迫下线。");
            }

            // 将session以用户名为索引,放入map中
            map.put(personInfo.getAccount(), event.getSession());
            System.out.println("帐号" + personInfo.getAccount() + "在" + personInfo.getIp() + "登录。");
        }
    }

    /**
     * Session 属性删除
     *
     * @param event
     */
    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {

        String name = event.getName();

        // 注销
        if (name.equals(PersonInfo.SESSION_PERSON)) {
            // 将该 session 从 map 中移除
            PersonInfo personInfo = (PersonInfo) event.getValue();
            map.remove(personInfo.getAccount());
            System.out.println("帐号" + personInfo.getAccount() + "注销。");
        }
    }

    /**
     * Session 属性修改
     * 没有注销的情况下,用另一个帐号登录
     *
     * @param event
     */
    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {

        // 被修改的属性名
        String name = event.getName();

        // 没有注销的情况下,用另一个帐号登录
        if (name.equals(PersonInfo.SESSION_PERSON)) {

            // 移除旧的的登录信息
            PersonInfo oldPersonInfo = (PersonInfo) event.getValue();
            map.remove(oldPersonInfo.getAccount());

            // 新的登录信息
            PersonInfo personInfo = (PersonInfo) event.getSession().getAttribute(PersonInfo.SESSION_PERSON);

            // 也要检查新登录的帐号是否在别的机器上登录过
            if (map.get(personInfo.getAccount()) != null) {
                // map 中有记录,表明该帐号在其他机器上登录过,将以前的登录失效
                HttpSession session = map.get(personInfo.getAccount());
                session.removeAttribute(PersonInfo.SESSION_PERSON);
                session.setAttribute(PersonInfo.SESSION_MSG, "您的帐号已经在其他机器上登录,您被迫下线。");
            }
            map.put(PersonInfo.SESSION_PERSON, event.getSession());
        }

    }


    class PersonInfo implements Serializable {

        private static final long serialVersionUID = 4063725584941336123L;

        public static final String SESSION_PERSON = "_PERSON_";
        public static final String SESSION_MSG = "_MSG_";

        /**
         * 帐号
         */
        private String account;

        /**
         * 登录IP地址
         */
        private String ip;

        /**
         * 登录时间
         */
        private Date loginDate;


        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            PersonInfo that = (PersonInfo) o;
            return Objects.equals(account, that.account) &&
                    Objects.equals(ip, that.ip) &&
                    Objects.equals(loginDate, that.loginDate);
        }

        @Override
        public int hashCode() {
            return Objects.hash(account, ip, 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;
        }
    }
}

显示在线人数: 在线用户列表、总访问人数等功能

  1. OnlineSessionListener: 在线人数监听器:监听Session对象属性变化,监听Session 属性变化。需要记录在线用户列表、总访问人数等
public class OnlineSessionListener implements HttpSessionListener, HttpSessionAttributeListener {

    /**
     * 监听 Session 创建
     *
     * @param sessionEvent
     */
    @Override
    public void sessionCreated(HttpSessionEvent sessionEvent) {

        HttpSession session = sessionEvent.getSession();

        // 将 session 放入 map
        OnlineConstants.SESSION_MAP.put(session.getId(), session);
        // 总访问人数++
        OnlineConstants.TOTAL_HISTORY_COUNT++;

        // 如果当前在线人数超过历史记录,则更新最大在线人数,并记录时间
        int sessionCount = OnlineConstants.SESSION_MAP.size();
        if (sessionCount > OnlineConstants.MAX_ONLINE_COUNT) {
            OnlineConstants.MAX_ONLINE_COUNT = sessionCount;
            OnlineConstants.MAX_ONLINE_COUNT_DATE = new Date();
        }
    }

    /**
     * 监听Session 销毁
     *
     * @param sessionEvent
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent sessionEvent) {
        HttpSession session = sessionEvent.getSession();
        // 将session从map中移除
        OnlineConstants.SESSION_MAP.remove(session.getId());
    }

    /**
     * 监听 Session 属性添加
     *
     * @param event
     */
    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {

        if (event.getName().equals(OnlineConstants.SESSION_PERSON)) {

            // 当前登录用户数++
            OnlineConstants.CURRENT_LOGIN_COUNT++;
            HttpSession session = event.getSession();

            // 查找该帐号有没有在其他机器上登录
            for (HttpSession oldSession : OnlineConstants.SESSION_MAP.values()) {

                // 如果该帐号已经在其他机器上登录,则以前的登录失效
                if (event.getValue().equals(oldSession.getAttribute(OnlineConstants.SESSION_PERSON))
                        && session.getId() != oldSession.getId()) {
                    oldSession.invalidate();
                }
            }
        }
    }

    /**
     * 监听 Session 属性删除
     *
     * @param event
     */
    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {

        // 注销 当前登录用户数--
        if (event.getName().equals(OnlineConstants.SESSION_PERSON)) {
            OnlineConstants.CURRENT_LOGIN_COUNT--;
        }
    }

    /**
     * 监听 Session 属性修改
     *
     * @param event
     */
    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {

        // 重新登录
        if (event.getName().equals(OnlineConstants.SESSION_PERSON)) {
            HttpSession session = event.getSession();
            for (HttpSession sess : OnlineConstants.SESSION_MAP.values()) {
                // 如果新帐号在其他机器上登录过,则以前登录失效
                if (event.getValue().equals(sess.getAttribute("personInfo"))
                        && session.getId() != sess.getId()) {
                    sess.invalidate();
                }
            }
        }
    }

}

  1. OnlineRequestListener: 在线人数监听器:监听 ServletRequest 创建与销毁。统计单用户,访问次数。
public class OnlineRequestListener implements ServletRequestListener {

    /**
     * 每一个请求都会触发
     *
     * @param event
     */
    @Override
    public void requestInitialized(ServletRequestEvent event) {

        HttpServletRequest request = (HttpServletRequest) event.getServletRequest();

        HttpSession session = request.getSession(true);

        // 记录客户端 IP 地址
        session.setAttribute("ip", request.getRemoteAddr());

        // 记录访问次数,只记录访问 .html, .do, .jsp, .action 的累计次数
        String uri = request.getRequestURI();
        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);
    }


    @Override
    public void requestDestroyed(ServletRequestEvent event) {
    }
}

  1. OnlineContextListener:在线人数监听器:监听 ServletContext 启动停止
public class OnlineContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        // 启动时,记录服务器启动时间
        OnlineConstants.START_DATE = new Date();
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        // 关闭时,将结果清除。也可以将结果保存到硬盘上。
        OnlineConstants.START_DATE = null;
        OnlineConstants.MAX_ONLINE_COUNT_DATE = null;
    }
}

  1. OnlineConstants: 在线人数监听器:需要监听的类型信息
public class OnlineConstants {


    /**
     * 索引所有的 Session
     * 1、Session创建的时候,放入map
     * 2、Session销毁的时候,从map中删除
     */
    public static Map<String, HttpSession> SESSION_MAP = new HashMap<>();

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

    /**
     * 历史访客
     */
    public static int TOTAL_HISTORY_COUNT;

    /**
     * 服务器启动时间
     */
    public static Date START_DATE;

    /**
     * 最高在线时间
     */
    public static Date MAX_ONLINE_COUNT_DATE;

    /**
     * 最高在线人数
     */
    public static int MAX_ONLINE_COUNT;


    public static final String SESSION_PERSON = "_PERSON_";

}

参考

  1. 源码地址
  2. Servlet学习笔记(九):监听器Listener详解

Fork me on Gitee

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值