监听器应用【统计网站人数、自定义session扫描器、踢人小案例】

小编 2026-06-27 阅读:460 评论:0
从第一篇已经讲解过了监听器的基本概念,以及Servlet各种的监听器。这篇博文主要讲解的是监听器...

从第一篇已经讲解过了监听器的基本概念,以及Servlet各种的监听器。这篇博文主要讲解的是监听器的应用。

统计网站在线人数

分析

我们在网站中一般使用Session来标识某用户是否登陆了,如果登陆了,就在Session域中保存相对应的属性。如果没有登陆,那么Session的属性就应该为空。

现在,我们想要统计的是网站的在线人数。我们应该这样做:我们监听是否有新的Session创建了,如果新创建了Sesssion,那么在线人数就应该+1。这个在线人数是整个站点的,所以应该有Context对象保存。

大致思路:

  • 监听Session是否被创建了
  • 如果Session被创建了,那么在Context的域对象的值就应该+1
  • 如果Session从内存中移除了,那么在Context的域对象的值就应该-1.

代码

  • 监听器代码:
    public class CountOnline implements HttpSessionListener {            public void sessionCreated(HttpSessionEvent se) {                //获取得到Context对象,使用Context域对象保存用户在线的个数            ServletContext context = se.getSession().getServletContext();                        //直接判断Context对象是否存在这个域,如果存在就人数+1,如果不存在,那么就将属性设置到Context域中            Integer num = (Integer) context.getAttribute("num");                        if (num == null) {                context.setAttribute("num", 1);            } else {                num++;                context.setAttribute("num", num);            }        }        public void sessionDestroyed(HttpSessionEvent se) {                ServletContext context = se.getSession().getServletContext();            Integer num = (Integer) se.getSession().getAttribute("num");                if (num == null) {                context.setAttribute("num", 1);            } else {                num--;                context.setAttribute("num", num);            }        }    }
  • 显示页面代码:
在线人数:${num}

测试

我们每使用一个浏览器访问服务器,都会新创建一个Session。那么网站的在线人数就会+1。

使用同一个页面刷新,还是使用的是那个Sesssion,所以网站的在线人数是不会变的。

监听器应用【统计网站人数、自定义session扫描器、踢人小案例】


自定义Session扫描器

我们都知道Session是保存在内存中的,如果Session过多,服务器的压力就会非常大。

但是呢,Session的默认失效时间是30分钟(30分钟没人用才会失效),这造成Seesion可能会过多(没人用也存在内存中,这不是明显浪费吗?)

当然啦,我们可以在web.xml文件中配置Session的生命周期。但是呢,这是由服务器来做的,我嫌它的时间不够准确。(有时候我配置了3分钟,它用4分钟才帮我移除掉Session)

所以,我决定自己用程序手工移除那些长时间没人用的Session。

分析

要想移除长时间没人用的Session,肯定要先拿到全部的Session啦。所以我们使用一个容器来装载站点所有的Session。。

只要Sesssion一创建了,就把Session添加到容器里边。毫无疑问的,我们需要监听Session了。

接着,我们要做的就是隔一段时间就去扫描一下全部Session,如果有Session长时间没使用了,我们就把它从内存中移除。隔一段时间去做某事,这肯定是定时器的任务呀。

定时器应该在服务器一启动的时候,就应该被创建了。因此还需要监听Context

最后,我们还要考虑到并发的问题,如果有人同时访问站点,那么监听Session创建的方法就会被并发访问了定时器扫描容器的时候,可能是获取不到所有的Session的

这需要我们做同步

于是乎,我们已经有大致的思路了

  • 监听Session和Context的创建
  • 使用一个容器来装载Session
  • 定时去扫描Session,如果它长时间没有使用到了,就把该Session从内存中移除。
  • 并发访问的问题

代码

  • 监听器代码:
    public class Listener1 implements ServletContextListener,            HttpSessionListener {                    //服务器一启动,就应该创建容器。我们使用的是LinkList(涉及到增删)。容器也应该是线程安全的。        List<HttpSession> list = Collections.synchronizedList(new LinkedList<HttpSession>());            //定义一把锁(Session添加到容器和扫描容器这两个操作应该同步起来)        private Object lock = 1;            public void contextInitialized(ServletContextEvent sce) {                    Timer timer = new Timer();            //执行我想要的任务,0秒延时,每10秒执行一次            timer.schedule(new MyTask(list, lock), 0, 10 * 1000);            }        public void sessionCreated(HttpSessionEvent se) {                //只要Session一创建了,就应该添加到容器中            synchronized (lock) {                list.add(se.getSession());            }            System.out.println("Session被创建啦");            }            public void sessionDestroyed(HttpSessionEvent se) {            System.out.println("Session被销毁啦。");        }        public void contextDestroyed(ServletContextEvent sce) {            }    }
  • 任务代码:
    /*    * 在任务中应该扫描容器,容器在监听器上,只能传递进来了。    *    * 要想得到在监听器上的锁,也只能是传递进来    *    * */    class MyTask extends TimerTask {            private List<HttpSession> sessions;        private Object lock;            public MyTask(List<HttpSession> sessions, Object lock) {            this.sessions = sessions;            this.lock = lock;        }            @Override        public void run() {                synchronized (lock) {                //遍历容器                for (HttpSession session : sessions) {                        //只要15秒没人使用,我就移除它啦                    if (System.currentTimeMillis() - session.getLastAccessedTime() > (1000 * 15)) {                        session.invalidate();                        sessions.remove(session);                    }                    }            }        }    }
  • 测试:

15秒如果Session没有活跃,那么就被删除!

监听器应用【统计网站人数、自定义session扫描器、踢人小案例】

  • 使用集合来装载我们所有的Session
  • 使用定时器来扫描session的声明周期【由于定时器没有session,我们传进去就好了】
  • 关于并发访问的问题,我们在扫描和检测session添加的时候,同步起来就好了【当然,定时器的锁也是要外面传递进来的】

踢人小案列

列出所有的在线用户,后台管理者拥有踢人的权利,点击踢人的超链接,该用户就被注销了。

分析

首先,怎么能列出所有的在线用户呢??一般我们在线用户都是用Session来标记的,所有的在线用户就应该用一个容器来装载所有的Session。。

我们监听Session的是否有属性添加(监听Session的属性有添加、修改、删除三个方法。如果监听到Session添加了,那么这个肯定是个在线用户!)。

装载Session的容器应该是在Context里边的【属于全站点】,并且容器应该使用Map集合【待会还要通过用户的名字来把用户踢了】

思路:

  • 写监听器,监听是否有属性添加在Session里边了
  • 写简单的登陆页面。
  • 列出所有的在线用户
  • 实现踢人功能(也就是摧毁Session)

代码

  • 监听器
public class KickPerson implements HttpSessionAttributeListener {    // Public constructor is required by servlet spec    public KickPerson() {    }    public void attributeAdded(HttpSessionBindingEvent sbe) {        //得到context对象,看看context对象是否有容器装载Session        ServletContext context = sbe.getSession().getServletContext();        //如果没有,就创建一个呗        Map map = (Map) context.getAttribute("map");        if (map == null) {            map = new HashMap();            context.setAttribute("map", map);        }        //---------------------------------------------------------------------------------------                //得到Session属性的值        Object o = sbe.getValue();        //判断属性的内容是否是User对象        if (o instanceof User) {            User user = (User) o;            map.put(user.getUsername(), sbe.getSession());        }    }    public void attributeRemoved(HttpSessionBindingEvent sbe) {      /* This method is called when an attribute         is removed from a session.      */    }    public void attributeReplaced(HttpSessionBindingEvent sbe) {      /* This method is invoked when an attibute         is replaced in a session.      */    }}
  • 登陆页面
<form action="${pageContext.request.contextPath }/LoginServlet" method="post">    用户名:<input type="text" name="username">    <input type="submit" value="登陆"></form>
  • 处理登陆Servlet
        //得到传递过来的数据        String username = request.getParameter("username");        User user = new User();        user.setUsername(username);        //标记该用户登陆了!        request.getSession().setAttribute("user", user);        //提供界面,告诉用户登陆是否成功        request.setAttribute("message", "恭喜你,登陆成功了!");        request.getRequestDispatcher("/message.jsp").forward(request, response);
  • 列出在线用户
<c:forEach items="${map}" var="me">    ${me.key} <a href="${pageContext.request.contextPath}/KickPersonServlet?username=${me.key}">踢了他吧</a>    <br></c:forEach>
  • 处理踢人的Servlet
        String username = request.getParameter("username");        //得到装载所有的Session的容器        Map map = (Map) this.getServletContext().getAttribute("map");        //通过名字得到Session        HttpSession httpSession = (HttpSession) map.get(username);        httpSession.invalidate();        map.remove(username);        //摧毁完Session后,返回列出在线用户页面        request.getRequestDispatcher("/listUser.jsp").forward(request, response);

测试

使用多个浏览器登陆来模拟在线用户(同一个浏览器使用的都是同一个Session)

监听器应用【统计网站人数、自定义session扫描器、踢人小案例】


监听Seesion的创建和监听Session属性的变化有啥区别???

  • Session的创建只代表着浏览器给服务器发送了请求。会话建立
  • Session属性的变化就不一样了,登记的是具体用户是否做了某事(登陆、购买了某商品)

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章的同学,可以关注微信公众号:Java3y

更多的文章可往:文章的目录导航
版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表