zoukankan      html  css  js  c++  java
  • javaweb学习总结(四十七)——监听器(Listener)在开发中的应用

      监听器在JavaWeb开发中用得比较多,下面说一下监听器(Listener)在开发中的常见应用

    一、统计当前在线人数

      在JavaWeb应用开发中,有时候我们需要统计当前在线的用户数,此时就可以使用监听器技术来实现这个功能了。

     1 package me.gacl.web.listener;
     2 
     3 import javax.servlet.ServletContext;
     4 import javax.servlet.http.HttpSessionEvent;
     5 import javax.servlet.http.HttpSessionListener;
     6 
     7 /**
     8 * @ClassName: OnLineCountListener
     9 * @Description: 统计当前在线用户个数
    10 * @author: 孤傲苍狼
    11 * @date: 2014-9-10 下午10:01:26
    12 *
    13 */ 
    14 public class OnLineCountListener implements HttpSessionListener {
    15 
    16     @Override
    17     public void sessionCreated(HttpSessionEvent se) {
    18         ServletContext context = se.getSession().getServletContext();
    19         Integer onLineCount = (Integer) context.getAttribute("onLineCount");
    20         if(onLineCount==null){
    21             context.setAttribute("onLineCount", 1);
    22         }else{
    23             onLineCount++;
    24             context.setAttribute("onLineCount", onLineCount);
    25         }
    26     }
    27 
    28     @Override
    29     public void sessionDestroyed(HttpSessionEvent se) {
    30         ServletContext context = se.getSession().getServletContext();
    31         Integer onLineCount = (Integer) context.getAttribute("onLineCount");
    32         if(onLineCount==null){
    33             context.setAttribute("onLineCount", 1);
    34         }else{
    35             onLineCount--;
    36             context.setAttribute("onLineCount", onLineCount);
    37         }
    38     }
    39 }

    二、自定义Session扫描器

      当一个Web应用创建的Session很多时,为了避免Session占用太多的内存,我们可以选择手动将这些内存中的session销毁,那么此时也可以借助监听器技术来实现。

      1 package me.gacl.web.listener;
      2 
      3 import java.util.Collections;
      4 import java.util.LinkedList;
      5 import java.util.List;
      6 import java.util.ListIterator;
      7 import java.util.Timer;
      8 import java.util.TimerTask;
      9 import javax.servlet.ServletContextEvent;
     10 import javax.servlet.ServletContextListener;
     11 import javax.servlet.http.HttpSession;
     12 import javax.servlet.http.HttpSessionEvent;
     13 import javax.servlet.http.HttpSessionListener;
     14 
     15 /**
     16 * @ClassName: SessionScanerListener
     17 * @Description: 自定义session扫描器
     18 * @author: 孤傲苍狼
     19 * @date: 2014-9-10 下午10:16:42
     20 * 
     21 */ 
     22 public class SessionScanerListener implements HttpSessionListener,ServletContextListener {
     23 
     24     /**
     25     * @Field: list
     26     *          定义一个集合存储服务器创建的HttpSession
     27     *        LinkedList不是一个线程安全的集合
     28     */ 
     29     /**
     30      * private List<HttpSession> list = new LinkedList<HttpSession>();
     31      * 这样写涉及到线程安全问题,SessionScanerListener对象在内存中只有一个
     32      * sessionCreated可能会被多个人同时调用,
     33      * 当有多个人并发访问站点时,服务器同时为这些并发访问的人创建session
     34      * 那么sessionCreated方法在某一时刻内会被几个线程同时调用,几个线程并发调用sessionCreated方法
     35      * sessionCreated方法的内部处理是往一个集合中添加创建好的session,那么在加session的时候就会
     36      * 涉及到几个Session同时抢夺集合中一个位置的情况,所以往集合中添加session时,一定要保证集合是线程安全的才行
     37      * 如何把一个集合做成线程安全的集合呢?
     38      * 可以使用使用 Collections.synchronizedList(List<T> list)方法将不是线程安全的list集合包装线程安全的list集合
     39      */
     40     //使用 Collections.synchronizedList(List<T> list)方法将LinkedList包装成一个线程安全的集合
     41     private List<HttpSession> list = Collections.synchronizedList(new LinkedList<HttpSession>());
     42     //定义一个对象,让这个对象充当一把锁,用这把锁来保证往list集合添加的新的session和遍历list集合中的session这两个操作达到同步
     43     private Object lock = new Object();
     44             
     45     @Override
     46     public void sessionCreated(HttpSessionEvent se) {
     47         System.out.println("session被创建了!!");
     48         HttpSession session = se.getSession();
     49         
     50         synchronized (lock){
     51             /**
     52              *将该操作加锁进行锁定,当有一个thread-1(线程1)在调用这段代码时,会先拿到lock这把锁,然后往集合中添加session,
     53              *在添加session的这个过程中假设有另外一个thread-2(线程2)来访问了,thread-2可能是执行定时器任务的,
     54              *当thread-2要调用run方法遍历list集合中的session时,结果发现遍历list集合中的session的那段代码被锁住了,
     55              *而这把锁正在被往集合中添加session的那个thread-1占用着,因此thread-2只能等待thread-1操作完成之后才能够进行操作
     56              *当thread-1添加完session之后,就把lock放开了,此时thread-2拿到lock,就可以执行遍历list集合中的session的那段代码了
     57              *通过这把锁就保证了往集合中添加session和变量集合中的session这两步操作不能同时进行,必须按照先来后到的顺序来进行。
     58              */
     59             list.add(session);
     60         }
     61     }
     62 
     63     @Override
     64     public void sessionDestroyed(HttpSessionEvent se) {
     65         System.out.println("session被销毁了了!!");
     66     }
     67 
     68     /* Web应用启动时触发这个事件
     69      * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
     70      */
     71     @Override
     72     public void contextInitialized(ServletContextEvent sce) {
     73         System.out.println("web应用初始化");
     74         //创建定时器
     75         Timer timer = new Timer();
     76         //每隔30秒就定时执行任务
     77         timer.schedule(new MyTask(list,lock), 0, 1000*30);
     78     }
     79 
     80     @Override
     81     public void contextDestroyed(ServletContextEvent sce) {
     82         System.out.println("web应用关闭");
     83     }
     84 }
     85 
     86 /**
     87 * @ClassName: MyTask
     88 * @Description:定时器要定时执行的任务
     89 * @author: 孤傲苍狼
     90 * @date: 2014-9-11 上午12:02:36
     91 *
     92 */ 
     93 class MyTask extends TimerTask {
     94         
     95     //存储HttpSession的list集合
     96     private List<HttpSession> list;
     97     //存储传递过来的锁
     98     private Object lock;
     99     public MyTask(List<HttpSession> list,Object lock){
    100         this.list = list;
    101         this.lock = lock;
    102     }
    103     /* run方法指明了任务要做的事情
    104      * @see java.util.TimerTask#run()
    105      */
    106     @Override
    107     public void run() {
    108             //将该操作加锁进行锁定
    109         synchronized (lock) {
    110             System.out.println("定时器执行!!");
    111             ListIterator<HttpSession> it = list.listIterator();
    112             /**
    113              * 迭代list集合中的session,在迭代list集合中的session的过程中可能有别的用户来访问,
    114              * 用户一访问,服务器就会为该用户创建一个session,此时就会调用sessionCreated往list集合中添加新的session,
    115              * 然而定时器在定时执行扫描遍历list集合中的session时是无法知道正在遍历的list集合又添加的新的session进来了,
    116              * 这样就导致了往list集合添加的新的session和遍历list集合中的session这两个操作无法达到同步
    117              * 那么解决的办法就是把"list.add(session)和while(it.hasNext()){//迭代list集合}"这两段代码做成同步,
    118              * 保证当有一个线程在访问"list.add(session)"这段代码时,另一个线程就不能访问"while(it.hasNext()){//迭代list集合}"这段代码
    119              * 为了能够将这两段不相干的代码做成同步,只能定义一把锁(Object lock),然后给这两步操作加上同一把锁,
    120              * 用这把锁来保证往list集合添加的新的session和遍历list集合中的session这两个操作达到同步
    121              * 当在执行往list集合添加的新的session操作时,就必须等添加完成之后才能够对list集合进行迭代操作,
    122              * 当在执行对list集合进行迭代操作时,那么必须等到迭代操作结束之后才能够往往list集合添加的新的session
    123              */
    124             while(it.hasNext()){
    125                 HttpSession session = (HttpSession) it.next();
    126                 /**
    127                  * 如果当前时间-session的最后访问时间>1000*15(15秒)
    128                  * session.getLastAccessedTime()获取session的最后访问时间
    129                  */
    130                 if(System.currentTimeMillis()-session.getLastAccessedTime()>1000*30){
    131                     //手动销毁session
    132                     session.invalidate();
    133                     //移除集合中已经被销毁的session
    134                     it.remove();
    135                 }
    136             }
    137         }
    138     }
    139 }

     以上就是监听器的两个简单应用场景。

  • 相关阅读:
    IIS6.0服务器架站无法访问解决方案总结
    DNN中做支持多语言的模块
    在dotnetnuke中创建 parent portal
    DNN,Rainbow资源
    2005年岁末,各种主流CMS系统的比较和汇总
    在DNN中获取所有模块信息
    学习dnn的新资源,sooooooooo great!!
    DNN的电子商务模块
    DNN学习笔记
    也学ASP.NET 2.0 AJAX 之二:使用Timer控件
  • 原文地址:https://www.cnblogs.com/xdp-gacl/p/3965508.html
Copyright © 2011-2022 走看看