JavaWeb了解之入门篇1-2(Servlet监听器)

目录
  1. 监听器
  2. JavaWeb中的监听器(Servlet监听器)
    1. 监听域对象(ServletContext、HttpSession、ServletRequest)的创建和销毁。
    2. 监听域对象中的属性的变更(添加、更新、删除)。
    3. 监听HttpSession域中的属性的状态。
  3. 常见的使用示例
    1. 示例(统计当前在线人数)
    2. 示例(遍历Session集合销毁过时的session)

1. 监听器

一个监听其他对象方法或属性改变的普通Java类(监听对象的方法或属性发生改变后会立即触发监听器的指定方法)。

事件(方法调用、属性改变、状态改变等)监听涉及:
  1. 事件源
    被监听的对象(如:request、session、servletContext等)。
  2. 事件对象
    从事件对象中可获取事件源。
  3. 事件监听器。
    用于监听事件源。
注册监听器(将事件监听器与事件源进行绑定)后,当事件源对象上发生某一事件时,会调用事件监听器的对应方法(事件对象作为方法参数)。

示例(监听window窗口的事件监听器)

package com.sst.cx;
import java.awt.Frame;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
public class TestListener {
    public static void main(String[] args) {
        // 被监听的对象
        Frame f = new Frame();
        f.setSize(400, 400);
        f.setVisible(true);
        // 添加监听(注册事件监听器)
        f.addWindowListener(new WindowListener(){
            // 窗口关闭时会调用
            public void windowClosing(WindowEvent e) {
                Frame f = (Frame) e.getSource(); // 通过事件对象e来获取事件源对象
                f.dispose();
            }
            // 窗口关闭后会调用
            public void windowClosed(WindowEvent e) {}
            // 窗口变为活跃状态时会调用
            public void windowActivated(WindowEvent e) {}
            // 窗口变为非活跃状态时会调用
            public void windowDeactivated(WindowEvent e) {}
            public void windowDeiconified(WindowEvent e) {}
            public void windowIconified(WindowEvent e) {}
            // 窗体打开后会调用
            public void windowOpened(WindowEvent e) {}
        });
    }
}

例(设计一个可以被监听的对象)

package com.sst.cx;
import java.awt.Frame;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
public class TestListener {
    public static void main(String[] args) {
        Person p = new Person();
        // 注册监听器
        p.registerListener(new PersonListener() {
            // 监听吃东西行为
            public void doeat(Event e) {
                Person p = e.getSource();
                System.out.println(p + "在吃东西");
            }
            // 监听跑步行为
            public void dorun(Event e) {
                Person p = e.getSource();
                System.out.println(p + "在跑步");
            }
        });
        p.eat();
        p.run();
    }
}
/**
Person(事件源)对象的行为(吃饭、跑步)可以被其他的对象监听
*/  
public class Person {
    // 存储监听器
    private PersonListener listener;
    // 注册的监听器
    public void registerListener(PersonListener listener) {
        this.listener = listener;
    }
    public void eat() {
        if (listener != null) {
            listener.doeat(new Event(this));
        }
    }
    public void run() {
        if (listener != null) {
            listener.dorun(new Event(this));
        }
    }
}
/**
PersonListener(事件监听器)接口,监听Person类事件源
*/ 
interface PersonListener {
    void doeat(Event e);
    void dorun(Event e);
}
/**
Event(事件对象) 用来封装事件源
*/ 
class Event {
    private Person source;
    public Event() {
    }
    public Event(Person source) {
        this.source = source;
    }
    public Person getSource() {
        return source;
    }
    public void setSource(Person source) {
        this.source = source;
    }
}

2. JavaWeb中的监听器(Servlet监听器)

Servlet规范中定义了8个监听器接口,用于
  1. 监听(ServletContext、HttpSession、ServletRequest)域对象的创建和销毁。
  2. 监听(ServletContext、HttpSession、ServletRequest)域对象中的属性的变更(添加、更新、删除)。
  3. 监听HttpSession域中的属性的状态变化。
创建Servlet监听器只需要实现相应的监听器接口并重写接口中的方法,创建后还需要在web.xml配置文件或使用@WebListener注解进行注册。
  1. 监听域对象的创建和销毁
Servlet规范中定义了监听ServletContext、HttpSession、HttpServletRequest这3个域对象的创建和销毁事件的监听器分别为:
  1. ServletContextListener
    void contextInitialized(ServletContextEvent sce)
    (服务器启动后会为每个Web应用)创建ServletContext域对象时会触发。
    void contextDestroyed(ServletContextEvent sce)
    (服务器关闭或应用被移除时会)销毁ServletContext域对象时会触发。
  2. HttpSessionListener
    void sessionCreated(HttpSessionEvent se)
    (第一次使用HttpServletRequest.getSession()方法时会)创建Session对象时会触发。
    void sessionDestroyed(HttpSessionEvent se)
    (Session过期;手动销毁;服务器关闭、应用被移除 时会)销毁Session对象时会触发。
  3. ServletRequestListener
    void requestInitialized(ServletRequestEvent sre)
    (用户每一次请求都会)创建request对象时会触发方法。
    void requestDestroyed(ServletRequestEvent sre)
    (当服务器将响应返回给客户端时会)销毁request对象时会触发方法。

例(监听ServletContext域对象的创建和销毁)

创建监听器类(实现ServletContextListener接口)并注册(通过web.xml配置文件注册 或 使用注解方式注册)

第1步. 创建监听类
package com.sst.cx;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
// 使用注解方式注册
// @WebListener
public class MyServletContextListener implements ServletContextListener{
    @Override
    public void contextDestroyed(ServletContextEvent arg0) { // ServletContext对象销毁
    }
    @Override
    public void contextInitialized(ServletContextEvent arg0) { // ServletContext对象创建
    } 
}
第2步. web.xml中+ (使用web.xml配置文件方式注册)
 
   ServletContextListener监听器
   com.sst.cx.MyServletContextListener
 

例(监听HttpSession域对象的创建和销毁)

创建监听器类(实现HttpSessionListener接口)并注册

// 创建监听器类
package com.sst.cx;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
// 注册监听器类
@WebListener
public class MyHttpSessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) { 
        System.out.println( se.getSession() + "=====创建了!!");
    }
    /* 
    HttpSession的销毁时机需要在web.xml中进行配置,如下:
          
              1 1分钟后销毁
          标签同级。
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println( se.getSession() +"=====销毁了!!");
    }   
}
可在jsp中测试:${pageContext.session.id}

例(监听ServletRequest对象的创建和销毁)

创建监听器类(实现ServletRequestListener接口)并注册

// 创建监听器类
package com.sst.cx;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
// 注册监听器类
@WebListener
public class MyServletRequestListener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println(sre.getServletRequest() + "销毁了!!");   
    }
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println(sre.getServletRequest() + "创建了!!");
    }
}
在jsp中测试 
  刷新一下jsp,控制台就会立刻输出Request对象的创建和销毁信息。
  用户每一次访问都会创建request对象,当访问结束后(即service方法调用完毕),request对象就会销毁。
  1. 监听域对象中的属性的变更(添加、更新、删除)
Servlet规范定义了监听ServletContext、HttpSession、HttpServletRequest这3个域对象的属性变更事件(属性的添加、更新、删除)的监听器分别为
  1. ServletContextAttributeListener
    public void attributeAdded(ServletContextAttributeEvent scae)方法。
      添加属性时会触发。
    public void attributeReplaced(ServletContextAttributeEvent scae)方法。
      更新属性值时会触发。
    public void attributeRemoved(ServletContextAttributeEvent scae)方法。
      删除属性时会触发。
  2. HttpSessionAttributeListener
    public void attributeAdded(HttpSessionBindingEvent  hsbe)方法。
      添加属性时会触发。
    public void attributeReplaced(HttpSessionBindingEvent  hsbe)方法。
      更新属性值时会触发。
    public void attributeRemoved(HttpSessionBindingEvent  hsbe)方法。
      删除属性时会触发。
  3. ServletRequestAttributeListener。
    public void attributeAdded(ServletRequestAttributeEvent srae)方法。
      添加属性时会触发。
    public void attributeReplaced(ServletRequestAttributeEvent srae)方法。
      更新属性值时会触发。
    public void attributeRemoved(ServletRequestAttributeEvent srae)方法。
      删除属性时会触发。
可以看到同一个事件在这三个接口中对应的方法名完全相同,只是参数类型不同。

第1步. 创建监听类
package com.sst.cx.listener;
import java.text.MessageFormat;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
public class MyAttributeListener implements ServletContextAttributeListener,HttpSessionAttributeListener,ServletRequestAttributeListener {
    // ServletContext
    @Override
    public void attributeAdded(ServletContextAttributeEvent scab) {
        String str =MessageFormat.format(
                "ServletContext域对象中添加了属性:{0},属性值是:{1}"
                ,scab.getName()
                ,scab.getValue());
        System.out.println(str);
    }
    @Override
    public void attributeRemoved(ServletContextAttributeEvent scab) {
        String str =MessageFormat.format(
                "ServletContext域对象中删除属性:{0},属性值是:{1}"
                ,scab.getName()
                ,scab.getValue());
        System.out.println(str);
    }
    @Override
    public void attributeReplaced(ServletContextAttributeEvent scab) {
        String str =MessageFormat.format(
                "ServletContext域对象中替换了属性:{0}的值"
                ,scab.getName());
        System.out.println(str);
    }
    //  HttpSession
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        String str =MessageFormat.format(
                "HttpSession域对象中添加了属性:{0},属性值是:{1}"
                ,se.getName()
                ,se.getValue());
        System.out.println(str);
    }
    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        String str =MessageFormat.format(
                "HttpSession域对象中删除属性:{0},属性值是:{1}"
                ,se.getName()
                ,se.getValue());
        System.out.println(str);
    }
    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        String str =MessageFormat.format(
                "HttpSession域对象中替换了属性:{0}的值"
                ,se.getName());
        System.out.println(str);
    }
    // HttpServletRequest
    @Override
    public void attributeAdded(ServletRequestAttributeEvent srae) {
        String str =MessageFormat.format(
                "ServletRequest域对象中添加了属性:{0},属性值是:{1}"
                ,srae.getName()
                ,srae.getValue());
        System.out.println(str);
    }
    @Override
    public void attributeRemoved(ServletRequestAttributeEvent srae) {
        String str =MessageFormat.format(
                "ServletRequest域对象中删除属性:{0},属性值是:{1}"
                ,srae.getName()
                ,srae.getValue());
        System.out.println(str);
    }
    @Override
    public void attributeReplaced(ServletRequestAttributeEvent srae) {
        String str =MessageFormat.format(
                "ServletRequest域对象中替换了属性:{0}的值"
                ,srae.getName());
        System.out.println(str);
    }
}
第2步. web.xml 同上
第3步. jsp中测试
  <%
    application.setAttribute("name", "zhangsan");
    application.setAttribute("name", "lisi");
    application.removeAttribute("name");

    session.setAttribute("name", "zhangsan");
    session.setAttribute("name", "lisi");
    session.removeAttribute("name");

    request.setAttribute("name", "zhangsan");
    request.setAttribute("name", "lisi");
    request.removeAttribute("name");
  %>
  1. 监听HttpSession域中属性的状态
保存在HttpSession域中的对象有多种状态:
  1. 绑定到Session中
    session.setAttribute("bean",Object)
  2. 从Session中解除绑定
    session.removeAttribute("bean")
  3. 随Session对象持久化到硬盘中(钝化)
  4. 随Session对象从硬盘恢复到内存中(活化)

Servlet规范中定义了2个特殊的监听器(用来监听在Session中的对象的状态变化)
  1. HttpSessionBindingListener
    void valueBound(HttpSessionBindingEvent event)
      绑定到HttpSession对象中时会触发。
    void valueUnbound(HttpSessionBindingEvent event)
      从HttpSession对象中删除时会触发。
  2. HttpSessionActivationListener
    void sessionWillPassivate(HttpSessionEvent event)
      反序列化时(随HttpSession对象反序列化到内存)会触发。
    void sessionDidActive(HttpSessionEvent event)
      序列化时(随HttpSession对象序列化到硬盘)会触发。
      对于没有实现Serializable接口的JavaBean对象,在HttpSession对象钝化(序列化)时会被删除。
  注意:实现这两个监听器接口的类不需要在web.xml文件中进行注册。

例(HttpSessionBindingListener监听:绑定到Session对象、从Session中删除)

1. 创建监听类,并实现HttpSessionBindingListener接口,实现valueBound、valueUnbound方法。
package com.sst.cx;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
public class TestJavaBean implements HttpSessionBindingListener {
    private String name;
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println(name+"被加到session中了");
    }
    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println(name+"被session踢出来了");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public JavaBeanDemo1(String name) {
        this.name = name;
    }
}
2. 在jsp中测试
  <%
    session.setAttribute("person", new TestJavaBean("张三"));
    session.removeAttribute("name");
  %>

例(HttpSessionActivationListener监听 被反序列化、序列化)

1. 创建监听类,并实现HttpSessionActivationListener接口,实现sessionWillPassivate、sessionDidActive方法。
package com.sst.cx;
import java.io.Serializable;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
public class TestJavaBean implements HttpSessionActivationListener,
        Serializable {
    private String name;
    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        System.out.println(name+"和session一起被序列化到硬盘了,session的id是:"+se.getSession().getId());
    }
    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        System.out.println(name+"和session一起从硬盘反序列化到内存了,session的id是:"+se.getSession().getId());
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public JavaBeanDemo2(String name) {
        this.name = name;
    }
}

2. 在WebRoot/META-INF目录下创建一个context.xml文件
配置了1分钟后就将HttpSession对象序列化到本地硬盘的一个hello目录中(Tomcat服务器的work/Catalina/localhost/项目名/hello目录下可找到钝化的.session文件)

  
    
  


3. jsp中测试
  <%
    session.setAttribute("person", new TestJavaBean("张三"));
  %>
1分钟后没有人再次访问时,服务器会自动将这个HttpSession对象钝化(序列化)到硬盘上。
再次访问jsp页面后,服务器又会自动将已经钝化(序列化)到硬盘上的HttpSession对象重新活化(反序列化)回到内存中。

3. 常见的使用示例

  1. 示例(统计当前在线人数)
1. 创建监听类
package com.sst.cx;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class OnLineCountListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        ServletContext context = se.getSession().getServletContext();
        Integer onLineCount = (Integer) context.getAttribute("onLineCount");
        if(onLineCount==null){
            context.setAttribute("onLineCount", 1);
        }else{
            onLineCount++;
            context.setAttribute("onLineCount", onLineCount);
        }
    }
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        ServletContext context = se.getSession().getServletContext();
        Integer onLineCount = (Integer) context.getAttribute("onLineCount");
        if(onLineCount==null){
            context.setAttribute("onLineCount", 1);
        }else{
            onLineCount--;
            context.setAttribute("onLineCount", onLineCount);
        }
    }
}
2. 配置web.xml 
  1. 示例(遍历Session集合销毁过时的session)

当一个Web应用创建的Session很多时,为了避免Session占用太多的内存,可以选择手动将这些内存中的session销毁。

public class SessionScanerListener implements HttpSessionListener,ServletContextListener {
    // 定义一个集合(存储服务器创建的HttpSession)
    // 使用 Collections.synchronizedList(List list)方法将非线程安全的LinkedList集合包装成一个线程安全的集合。
    private List list = Collections.synchronizedList(new LinkedList());
    // 定义一个对象,让这个对象充当一把锁,用这把锁来保证往list集合添加的新的session和遍历list集合中的session这两个操作达到同步。
    private Object lock = new Object();

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("session被创建了!!");
        HttpSession session = se.getSession();
        synchronized (lock){
            list.add(session);
        }
    }
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("session被销毁了了!!");
    }
    // Web应用启动时触发这个事件
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("web应用初始化");
        // 创建定时器
        Timer timer = new Timer();
        // 每隔30秒就定时执行任务
        timer.schedule(new MyTask(list,lock), 0, 1000*30);
    }
    // Web应用关闭时触发这个事件
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("web应用关闭");
    }
}
class MyTask extends TimerTask {
    // 存储HttpSession的list集合
    private List list;
    // 存储传递过来的锁
    private Object lock;
    public MyTask(List list,Object lock){
        this.list = list;
        this.lock = lock;
    }
    // 定时器的任务
    @Override
    public void run() {
        synchronized (lock) {
            System.out.println("定时器执行!!");
            ListIterator it = list.listIterator();
            while(it.hasNext()){
                HttpSession session = (HttpSession) it.next();
                // 如果当前时间-session的最后访问时间>1000*30(30秒),则手动销毁session,并从list集合中移除
                // session.getLastAccessedTime()获取session的最后访问时间
                if(System.currentTimeMillis()-session.getLastAccessedTime()>1000*30){
                    session.invalidate();
                    it.remove();
                }
            }
        }
    }
}

你可能感兴趣的:(JavaWeb了解之入门篇1-2(Servlet监听器))