Servlet 知识详解(五)之 —— Listener对象 监听器 学习笔记

Stella981
• 阅读 459

本文查阅方法:
    1、查阅目录 —— 查阅本文目录,确定想要查阅的目录标题
    2、快捷“查找” —— 在当前浏览器页面,按键 “Ctrl+F” 按键组合,开启浏览器的查找功能,
             在查找搜索框中 输入需要查阅的 目录标题,便可以直接到达 标题内容 的位置。
    3、学习小结 —— 文中的学习小结内容,是笔者在学习之后总结出的,开发时可直接参考其进行应用开发的内容, 进一步加快了本文的查阅 速度。(水平有限,仅供参考。)


本文目录

    学习小结

    1、监听器的概念与原理图解

    2、观察者设计模式(Observer设计模式 )

    3、Servlet监听器 

    4、监听servletContext域对象创建和销毁 

    5、编写 Servlet 监听器 

    6、监听HttpSession域对象创建和销毁 

    7、监听HttpRequest域对象创建和销毁 

    8、Servlet监听器应用案例—— 统计当前在线人数

    9、Servlet监听器应用案例—— 自定义session扫描器控制其主动销毁

    10、监听三个域对象属性变化 

    11、attributeAdded 方法

    12、attributeRemoved 方法 

    13、attributeReplaced 方法 

    14、感知 Session 绑定的事件监听器 

    15、HttpSessionBindingListener接口 

    16、HttpSessionActivationListener接口 

    17、Servlet监听器应用案例—— 定时器模拟定时发邮件

    18、Servlet监听器应用案例—— 显示登陆用户列表,并实现踢人功能。

    《Servlet 知识详解(一)之  ——  ServletContext对象 和 ServletConfig对象 学习笔记》

        地址: http://even2012.iteye.com/blog/1838063

    《Servlet 知识详解(二)之  ——  Request对象 和 Response对象 学习笔记》

        地址:http://even2012.iteye.com/blog/1838099

    《Servlet 知识详解(三)之  ——  Cookie对象 和 Session对象  学习笔记》

        地址:http://even2012.iteye.com/blog/1838128

    《Servlet 知识详解(四)之  ——  Filter对象 过滤器  学习笔记》

        地址:http://even2012.iteye.com/blog/1963466

    《Servlet 知识详解(五)之  ——  Listener对象 监听器  学习笔记》

        地址:http://even2012.iteye.com/blog/1963467


 

    学习小结


1、监听器的概念与原理图解

        监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。

    面试题:请描述一下java事件监听机制。

        (1) Java的事件监听机制涉及到三个组件:事件源、事件监听器、事件对象

        (2) 当事件源上发生操作时,它将会调用事件监听器的一个方法,并在调用这个方法时,会传递事件对象过来。

        (3) 事件监听器由开发人员编写,开发人员在事件监听器中,通过事件对象可以拿到事件源,从而对事件源上的操作进行处理。

Servlet 知识详解(五)之 —— Listener对象 监听器 学习笔记  

监听器典型案例:监听window窗口的事件监听器


2、观察者设计模式(Observer设计模式 )

class Person{

      private PersonListener listener;

      public void registerListener(PersonListener listener){

            this.listener = listener;

      }

      public void run(){

            if(listener!=null){

                  Even even = new Even(this);

                  this.listener.dorun(even);

            }

            System.out.println("runn!!");

      }

      public void eat(){

            if(listener!=null){

                  Even e = new Even(this);

                  this.listener.doeat(e);

            }

            System.out.println("eat!!");

      }

}

// 监听器接口:Interface 

interface PersonListener{

      public void dorun(Even even);

      public void doeat(Even even);  

}

// 代表事件源的对象

class Even{

          private Person person;         

          public Even() {

                super();

                // TODO Auto-generated constructor stub

          }

          public Even(Person person) {

                super();

                this.person = person;

          }

          public Person getPerson() {

                return person;

          }

          public void setPerson(Person person) {

                this.person = person;

          }  

}

 // 实现监听器功能的类

class MyListener1 implements PersonListener{

      public void doeat(Even even) {

            System.out.println(even.getPerson()+"你天天吃,你就知道吃,你猪啊!!");

      }

      public void dorun(Even even) {

            System.out.println(even.getPerson()+"你吃完就跑,有病!!"); 

      } 

}

    观察者模式应用案例:餐馆有多部点菜设备,和一台已点菜单打印机,要求每台点菜设备点完菜,打印机都会自动打印出菜单给厨房,请问如何设计该自动打印系统 。

方案一:定时器扫描菜单数据库(缺点:会造成CUP空转——没有点菜的时间也会扫描数据库。)

方案二:采用观察者模式——每次点菜设备执行点菜操作后,都会触发监听器,使打印机主动打印菜单。


3、Servlet监听器 

        在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为 ServletContext, HttpSession 和 ServletRequest 这三个域对象。 

        Servlet规范针对这三个对象上的操作,又把这多种类型的监听器划分为三种类型。

            (1) 监听三个域对象创建和销毁的事件监听器

            (2) 监听域对象中属性的增加和删除的事件监听器

            (3) 监听绑定到 HttpSession 域中的某个对象的状态的事件监听器。(查看API文档)


4、监听servletContext域对象创建和销毁 

ServletContextListener 接口用于监听 ServletContext 对象的创建和销毁事件。

        (1) 当 ServletContext 对象被创建时,激发contextInitialized (ServletContextEvent sce)方法

        (2) 当 ServletContext 对象被销毁时,激发contextDestroyed(ServletContextEvent sce)方法。

提问,servletContext域对象何时创建和销毁:

创建:服务器启动时会针对每一个web应用创建servletcontext

销毁:服务器关闭前会先关闭代表每一个web应用的servletContext


5、编写 Servlet 监听器 

        和编写其它事件监听器一样,编写servlet监听器也需要实现一个特定的接口,并针对相应动作覆盖接口中的相应方法。

        和其它事件监听器略有不同的是,servlet监听器的注册不是直接注册在事件源上,而是由WEB容器负责注册,开发人员只需在web.xml文件中使用标签配置好监听器,web容器就会自动把监听器注册到事件源中。

        一个 web.xml 文件中可以配置多个 Servlet 事件监听器,web 服务器按照它们在 web.xml 文件中的注册顺序来加载和注册这些 Serlvet 事件监听器。

Demo样例: 

  

        cn.itcast.web.listener.exapmle.SendMailTimer

  


6、监听HttpSession域对象创建和销毁 

    (1) HttpSessionListener接口用于监听HttpSession的创建和销毁

        (a) 创建一个Session时,sessionCreated(HttpSessionEvent se) 方法将会被调用。

        (b) 销毁一个Session时,sessionDestroyed (HttpSessionEvent se) 方法将会被调用。

    (2) Session域对象创建和销毁的时机

        创建:用户第一次访问时,服务器创建session

        销毁:如果用户的session 30分钟没有使用,服务器就会销毁session,我们在web.xml里面也可以配置session失效时间

    (3) 配置Session失效时间:

          

                1

          

    (4) 配置Session对象的非活动时间:

        需要对服务器进行配置:将下面的代码创建一份context.xml文件,放到META-INF文件夹下面(会被发布到Tomcat服务器的conf\Catalina\localhost)

        

            

                

            

         


7、监听HttpRequest域对象创建和销毁 

    (1) ServletRequestListener 接口用于监听ServletRequest 对象的创建和销毁。

         (a) Request 对象被创建时,监听器的requestInitialized方法将会被调用。

         (b) Request 对象被销毁时,监听器的requestDestroyed方法将会被调用。

     (2) servletRequest域对象创建和销毁的时机:

         (a) 创建:用户每一次访问,都会创建一个reqeust

         (b) 销毁:当前访问结束,request对象就会销毁


8、Servlet监听器应用案例—— 统计当前在线人数

Demo样例:CountNumListener.java

public class CountNumListener implements HttpSessionListener { 

      public void sessionCreated(HttpSessionEvent se) {

            ServletContext context = se.getSession().getServletContext();

            Integer count = (Integer) context.getAttribute("count");

            if(count==null){

                  count = 1;

                  context.setAttribute("count", count);

            }else{

                  count++;

                  context.setAttribute("count", count);

            }

      }

      public void sessionDestroyed(HttpSessionEvent se) {

            ServletContext context = se.getSession().getServletContext();

            Integer count = (Integer) context.getAttribute("count");

            count--;

            context.setAttribute("count", count); 

      }

}


9、Servlet监听器应用案例—— 自定义session扫描器控制其主动销毁

Demo样例:SessionScanner.java

    public class SessionScanner implements HttpSessionListener,ServletContextListener { 

              private List list = Collections.synchronizedList(new LinkedList());

              private Object lock = new Object();

              // Web应用启动时,启动定时器!

              public void contextInitialized(ServletContextEvent sce) {    

                    Timer timer = new Timer();

                    // 定时器任务:每隔30秒就扫描一次集合:是否有Session对象超时,若超时就会被处理(删除)

                    timer.schedule(new MyTask(list,lock), 0, 30*1000);

             }

          // Session创建的时候,就被添加到集合里面,进行控制管理

          public void sessionCreated(HttpSessionEvent se) {

                HttpSession session = se.getSession();

                System.out.println(session + "被创建了!!");

                synchronized (lock) {  //锁旗标——防止定时器中删除操作 发生“并发修改异常”。

                      list.add(session);

                }

          }

          public void sessionDestroyed(HttpSessionEvent se) {

                System.out.println(se.getSession() + "被销毁了");

          }

          public void contextDestroyed(ServletContextEvent sce) {

                // TODO Auto-generated method stub

          }

    }

    // 定时器任务

    class MyTask extends TimerTask{

          private List list;

          private Object lock; //接收锁旗标—— 防止定时器中删除操作 发生“并发修改异常”。

          public MyTask(List list,Object lock){

                this.list = list;

                this.lock = lock;

          }

      @Override

      public void run() {

            System.out.println("定时器执行!!");

            synchronized (this.lock) {

                  ListIterator it = list.listIterator();// 此处若使用Iterator,下面在删除操作时,就会发生“并发修改异常”。

                  while(it.hasNext()){

                        HttpSession session = (HttpSession) it.next();

                        if((System.currentTimeMillis()-session.getLastAccessedTime())>30*1000){

                              session.invalidate();

                              //list.remove(session);  //并发修改异常:ConcurrentModificationException

                              it.remove();

                        }

                  }

            }

      }

}


10、监听三个域对象属性变化 

        Servlet规范定义了监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器。

        这三个监听器接口分别是ServletContextAttributeListener ,HttpSessionAttributeListener ,ServletRequestAttributeListener

        这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同。 


11、attributeAdded 方法

      当向被监听器对象中增加一个属性时,web容器就调用事件监听器的 attributeAdded 方法进行相应,这个方法接受一个事件类型的参数,监听器可以通过这个参数来获得正在增加属性的域对象和被保存到域中的属性对象 

    各个域属性监听器中的完整语法定义为:

        public void attributeAdded(ServletContextAttributeEvent scae) 

        public void attributeReplaced(HttpSessionBindingEvent  hsbe) 

        public void attributeRmoved(ServletRequestAttributeEvent srae)


12、attributeRemoved 方法 

    当删除被监听对象中的一个属性时,web 容器调用事件监听器的这个方法进行相应处理

        各个域属性监听器中的完整语法定义为:

            public void attributeRemoved(ServletContextAttributeEvent scae) 

            public void attributeRemoved (HttpSessionBindingEvent  hsbe) 

            public void attributeRemoved (ServletRequestAttributeEvent srae)


13、attributeReplaced 方法 

    当监听器的域对象中的某个属性被替换时,web容器调用事件监听器的这个方法进行相应处理

        各个域属性监听器中的完整语法定义为:

                public void attributeReplaced(ServletContextAttributeEvent scae) 

                public void attributeReplaced (HttpSessionBindingEvent  hsbe) 

                public void attributeReplaced (ServletRequestAttributeEvent srae)


14、感知 Session 绑定的事件监听器 

        保存在 Session 域中的对象可以有多种状态:

            (1) 绑定到  Session 中;

            (2) 从 Session 域中解除绑定;

            (3) 随 Session 对象持久化到一个存储设备中(钝化);

            (4) 随 Session 对象从一个存储设备中恢复(活化)

        Servlet 规范中定义了两个特殊的监听器接口来帮助 JavaBean 对象了解自己在 Session 域中的这些状态:HttpSessionBindingListener接口和HttpSessionActivationListener接口 ,实现这两个接口的类不需要 web.xml 文件中进行注册


15、HttpSessionBindingListener接口 

        实现了HttpSessionBindingListener接口的 JavaBean 对象可以感知自己被绑定到 Session 中和从 Session 中删除的事件

        当对象被绑定到 HttpSession 对象中时,web 服务器调用该对象的  void valueBound(HttpSessionBindingEvent event) 方法

        当对象从 HttpSession 对象中解除绑定时,web 服务器调用该对象的 void valueUnbound(HttpSessionBindingEvent event)方法


16、HttpSessionActivationListener接口 

        实现了HttpSessionActivationListener接口的 JavaBean 对象可以感知自己被活化和钝化的事件

        当绑定到 HttpSession 对象中的对象将要随 HttpSession 对象被钝化之前,web 服务器调用如下方法sessionWillPassivate(HttpSessionBindingEvent event) 方法

        当绑定到 HttpSession 对象中的对象将要随 HttpSession 对象被活化之后,web 服务器调用该对象的 void sessionDidActive(HttpSessionBindingEvent event)方法

 【备注:JavaBean对象只有实现了Serializable接口后,才能被持久化到硬盘。】

  【小技巧:  配置Session对象的非活动时间:需要对服务器进行配置:将下面的代码创建一份context.xml文件,放到META-INF文件夹下面(会被发布到Tomcat服务器的conf\Catalina\localhost)

    

        

            

        

    

    】


17、Servlet监听器应用案例—— 定时器模拟定时发邮件

Demo样例:SendMailTimer.java

public class SendMailTimer implements ServletContextListener { 

  public void contextInitialized(ServletContextEvent sce) {

    Timer timer = new Timer();

    Calendar c = Calendar.getInstance();

    c.set(2011, 3, 7, 11, 54, 44);

    timer.schedule(new TimerTask(){

      @Override

      public void run() {

        System.out.println("发邮件!!!");        

      }

    }, c.getTime());

  }

  public void contextDestroyed(ServletContextEvent sce) {

    // TODO Auto-generated method stub

  }

}


18、Servlet监听器应用案例—— 显示登陆用户列表,并实现踢人功能。

 思路:

        (1) 收集登录用户——通过HttpSessionAttributeListener监听器将保存通过登录验证用户的Session对象添加到集合中;

        (2) 踢出非法用户——在集合中找到非法用户的Session对象,将其销毁。

Demo样例:UserListener.java

public class UserListener implements HttpSessionAttributeListener {

      public void attributeAdded(HttpSessionBindingEvent se) {    

            Map map = (Map) se.getSession().getServletContext().getAttribute("map");

            if(map==null){

                  map = new HashMap();

                  se.getSession().getServletContext().setAttribute("map", map);

            }

            Object object = se.getValue();

            if(object instanceof User){

                  User user = (User) object;

                  map.put(user.getUsername(), se.getSession());

            }    

      }

      public void attributeRemoved(HttpSessionBindingEvent se) {

            // TODO Auto-generated method stub    

      }

      public void attributeReplaced(HttpSessionBindingEvent se) {

            // TODO Auto-generated method stub    

      }

Demo样例:踢出非法用户KickServlet.java 

public class KickServlet extends HttpServlet {

  public void doGet(HttpServletRequest request, HttpServletResponse response)

      throws ServletException, IOException {

        String username = request.getParameter("username");

        username = new String(username.getBytes("iso8859-1"),"UTF-8");

        Map map = (Map) this.getServletContext().getAttribute("map");

        HttpSession session = (HttpSession) map.get(username);

        if(session!=null){

              session.invalidate();

              map.remove(username);

        }

        response.sendRedirect("/day21_kick/listuser.jsp");

  }

  public void doPost(HttpServletRequest request, HttpServletResponse response)

      throws ServletException, IOException {

    doGet(request, response);

  }

}


敬请评论

(1)若您觉得本文 有用处  —— 请留言评论,以坚定其他 IT童鞋 阅读本文的信心。

(2)若您觉得本文 没用处  —— 请留言评论,笔者将会改进不足,以便为大家整理更加好用的笔记。  


点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这