l 监听器

1.1 监听器:

1.1.1 监听器的概述:

什么是监听器:

监听器就是一个实现了特定接口的Java类.这个Java类用来监听另一个Java类的方法调用或者属性改变.当被监听的对象的发生上述的事件后.监听器某个方法就会立即执行.

监听器术语:

事件源 :被监听的对象.(汽车)
监听器 :监听的对象.(报警器)
绑定监听器:在事件源绑定监听器.(汽车上安装报警器)
事件 :就是事件源的改变.事件源一旦发生变化.事件就会传递给监听器对象.监听器的相应的方法就会执行.(踢了汽车一脚).
* 事件封装了事件源的改变.作用:在监听器对象中获得事件源对象.

监听器应用:

监听器应用主要是在图形化界面中.(AWT,Android)
* 图形化界面演示监听器的使用.

1.1.2 Servlet的监听器:

什么是Servlet的监听器:

监听器,过滤器都属于Servlet技术中的高级部分.
Servlet的监听器:在Servlet的规范中定义了多种类型的监听器.监听器用来分别监听ServletContext,HttpSession,ServletRequest三个域对象.

Servlet的规范中提供了8个监听器:
按功能进行划分将其分成三类:
一类 :监听三个域对象的创建和销毁的监听器.
二类 :监听三个域对象的属性变更的监听器.(属性添加,属性移除,属性替换).
三类 :监听HttpSession对象中的JavaBean的状态的改变.(绑定,解除绑定,钝化,活化)

与传统的监听器的不同:
事件源 :ServletContext,HttpSession,ServletRequest.
监听器 :Servlet规范中提供的一组监听器的接口.ServletContextListener,HttpSessionListener…
将事件源与监听器进行绑定:
进行配置:web.xml中完成配置即可.

1.1.3 一类:监听三个域对象的创建和销毁的监听器:

ServletContextListener:监听ServletContext域对象的创建和销毁.

方法:
监听器详细方法和案例配置 - 图1
监听ServletContext对象创建的方法:
监听器详细方法和案例配置 - 图2
监听ServletContext对象的销毁的方法:

ServletContext对象何时被创建和销毁的?
创建:
服务器启动的时候创建ServletContext对象.

销毁:
服务器关闭的时候.(项目从服务器中移除的时候.)

使用ServletContextListener:
步骤一:编写一个类实现ServletContextListener接口.
public class MyServletContextListener implements ServletContextListener{
@Override
/**
用来监听ServletContext对象的创建的方法:
*/
public void contextInitialized(ServletContextEvent sce) {
System.out.println(“ServletContext对象被创建了…”);
}

@Override
/
用来监听ServletContext对象的销毁的方法:
/
public void contextDestroyed(ServletContextEvent sce) {
System.out.println(“ServletContext对象被销毁的…”);
}
}
步骤二:将事件源与监听器绑定:
在web.xml中进行配置即可.


com.juwins.listener.MyServletContextListener


ServletContextListener监听器作用:
*
加载框架的配置文件:
*
Spring框架.
实现任务调度:
Timer:
*
schedule(TimerTask task,Date date,long p);
|—参数1:任务
|—参数2:日期
|—参数3:执行周期
*
schedule(TimerTask task,long time,long p);**
|—参数1:任务
|—参数2:延迟多久执行
|—参数3:执行周期

HttpSessionListener:监听HttpSession域对象创建和销毁.

方法:
监听器详细方法和案例配置 - 图3
监听HttpSession对象创建的方法:
监听器详细方法和案例配置 - 图4
监听HttpSession对象销毁的方法:

HttpSession何时被创建和销毁:
创建:
服务器端第一次调用getSession()方法时候才会创建HttpSession.

销毁:
session过期了.(默认过期时间:30分钟)
非正常关闭服务器.(正常关闭服务器HttpSession序列化到硬盘.)
手动调用了session的方法invalidate();销毁session.

问题:
* 访问一个Servlet会不会创建session对象.
* 不会的!
* 访问一个JSP页面会不会创建session对象.
* 会的!
* 访问一个HTML页面会不会创建session对象
* 不会的!

监听器的使用:
* 步骤一:编写一个类实现HttpSessionListener:
public class MyHttpSessionListener implements HttpSessionListener{

@Override
/
监听HttpSession对象创建的方法:
/
public void sessionCreated(HttpSessionEvent event) {
System.out.println(“HttpSession对象创建了…”);
}

@Override
/

监听HttpSession对象 销毁的方法
/
public void sessionDestroyed(HttpSessionEvent event) {
System.out.println(“HttpSession对象销毁了…”);
}

}

* 步骤二:在web.xml中完成配置


com.juwins.listener.MyHttpSessionListener


HttpSessionListener的作用:
进行session扫描.
**
记录网站的在线人数! setAttribute()**

ServletRequestListener:监听ServletRequest域对象创建和销毁.

方法:
监听器详细方法和案例配置 - 图5
监听ServletRequest对象创建的方法:
监听器详细方法和案例配置 - 图6
监听ServletRequest对象销毁的方法:

ServletRequest对象何时创建和销毁?
创建:
客户的向服务器发送了一次请求,那么服务器就会为这次请求创建一个request对象.
销毁:
当服务器为这次请求作出了响应之后,将request对象销毁了.

问题:
* 访问一个Servlet会不会创建request对象.
* 会!
* 访问一个JSP会不会创建request对象
* 会!
* 访问一个HTML会不会创建request对象
*会!!!访问必须是服务器路径

ServletRequestListener的使用:
* 步骤一:编写一个类实现ServletRequestListener.
public class MyServletRequestListener implements ServletRequestListener{

@Override
/
监听ServletRequest对象销毁的方法
/
public void requestDestroyed(ServletRequestEvent event) {
System.out.println(“ServletRequest对象销毁了…”);
}

@Override
/

监听ServletRequest对象创建的方法
/
public void requestInitialized(ServletRequestEvent event) {
System.out.println(“ServletRequest对象创建了…”);
}

}
* 步骤二:对监听器进行配置:


com.juwins.listener.MyServletRequestListener

1.1.4 二类:监听三个域对象的属性变更的监听器.(属性添加,属性移除,属性替换).

ServletContextAttributeListener:监听ServletContext中的属性变更.

方法:
监听器详细方法和案例配置 - 图7
监听ServletContext对象中的属性添加的方法:
监听器详细方法和案例配置 - 图8
监听ServletContext对象中的属性移除的方法:
监听器详细方法和案例配置 - 图9
* 监听ServletContext对象中的属性替换的方法:

HttpSessionAttributeListener:监听HttpSession中的属性变更.

方法:
监听器详细方法和案例配置 - 图10
监听HttpSession对象中属性添加的方法:
监听器详细方法和案例配置 - 图11
监听HttpSession对象中属性移除的方法:
监听器详细方法和案例配置 - 图12
* 监听HttpSession对象中属性替换的方法:

ServletRequestAttributeListener:监听ServletRequest中的属性变更.

方法:
监听器详细方法和案例配置 - 图13
监听ServletRequest对象中的属性添加的方法:
监听器详细方法和案例配置 - 图14
监听ServletRequest对象中的属性移除的方法:
监听器详细方法和案例配置 - 图15
* 监听ServletRequest对象中的属性替换的方法:

1.1.5 三类:监听HttpSession中对象的状态变化的监听器.(绑定,解除绑定,钝化,活化)

三类监听器特殊的特性:

1.监听HttpSession的对象.
监听向session中存java对象.
2.这类监听器不需要进行配置:
需要让JavaBean自己感知在session中状态.

HttpSessionBindingListener:监听HttpSession中的JavaBean的状态(绑定和解除绑定状态)

方法:
监听器详细方法和案例配置 - 图16
监听HttpSession中JavaBean的绑定的状态.
监听器详细方法和案例配置 - 图17
监听HttpSession中JavaBean的解除绑定的状态.

监听器使用:( HttpSessionBindingListener 只会监听session域中存储的是否是javabean,而不监听String类型或其他类型对象)
* 编写一个JavaBean.实现该接口.(不需要进行配置)
public class Bean1 implements HttpSessionBindingListener{
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public void valueBound(HttpSessionBindingEvent arg0) {
System.out.println(“对象与session绑定了…”);
}

@Override
public void valueUnbound(HttpSessionBindingEvent arg0) {
System.out.println(“对象与session解除绑定了…”);
}

}

HttpSessionActivationListener:监听HttpSession中JavaBean的状态(钝化和活化状态)

钝化:将对象序列化到硬盘.
活化:将硬盘对象反序列化过程.

方法:
监听器详细方法和案例配置 - 图18
监听HttpSession中JavaBean的钝化的方法:
监听器详细方法和案例配置 - 图19
监听HttpSession中JavaBean的活化的方法:

监听器使用:
public class Bean2 implements HttpSessionActivationListener{

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println(“对象被ssession活化了…”);
}

@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println(“对象被session钝化了…”);
}

}

Session的钝化实质是对于session的优化手段!!!
* 将长时间不使用的session使其序列化.

手动配置Session序列化:
* 手动配置Session的序列化通过标签标签.
* tomcat/conf/context.xml :代表所有的虚拟主机和所有虚拟路径按照这个配置来执行.
* tomcat/conf/Catalina/localhost/context.xml :代表所有localhost虚拟主机下的所有的项目都可以按照这个配置来执行.
* 工程META-INF/context.xml :代表只有当前的工程按照这个配置执行.

* 在context.xml中进行配置:

<!—
maxIdleSwap :1.代表如果session一分钟没有使用的话,将session序列化到硬盘.
directory :juwins. 将session序列化后,序列化文件保存在juwins的路径中.
—>
maxIdleSwap=”1”>


1.2 监听器综合案例:在线踢人案例:

1.2.1 在线踢人案例的需求分析:

  • 登录:
    当用户登录进来以后,显示到人员列表中(集合).
    采用哪种集合?
    List,Set,Map.如果单纯就是要去显示人员的列表.那么可以使用List集合就OK.但是我们当前的需求不行.应该采用Map集合.因为有key和value键值对.让Map的Key是用户.Map的Value是用户所对应的Session.
    将Map集合存放到ServletContext范围:
    当用户登录:对象与session绑定了.将用户和对象的session存入到Map列表中.
    当用户被踢下线:对象与session解除绑定了.将用户从Map列表中移除了.

    1.2.2 在线踢人案例的环境搭建及登录功能实现:

    创建表:

    CREATE DATABASE mydata;
    USE mydata;
    CREATE TABLE USER(
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(20),
    PASSWORD VARCHAR(20),
    nickname VARCHAR(20),
    TYPE VARCHAR(10)
    );

    INSERT INTO USER VALUES (NULL,’aaa’,’111’,’张三’,’user’);
    INSERT INTO USER VALUES (NULL,’bbb’,’111’,’李四’,’user’);
    INSERT INTO USER VALUES (NULL,’admin1’,’111’,’王五’,’admin’);
    INSERT INTO USER VALUES (NULL,’admin2’,’111’,’赵六’,’admin’);

    创建一个web项目,引入相应jar包:

    引入jar包:
    mysql数据库驱动包
    c3p0连接池的包
    jstl的包
    beanutils的包
    * dbutils的包.

    创建包结构:

  • com.juwins
    servlet
    LoginServlet
    service
    UserService
    dao
    UserDao
    domain
    User
    utils

    创建登录页面:

    登录页面















    用户名
    密码


    代码实现登录功能:

  • 在登录页面上点击登录按钮:
    提交到Servlet:
    接收数据:
    封装数据:
    调用业务层处理数据:
    页面跳转:
    登录成功:跳转到登录成功页面.
    * 登录失败:跳转到登录页面.

    代码实现人员列表显示:

    分析
    人员列表应该存放在一个Map集合中.
    Map集合存放在ServletContext域中.
    ServletContextListener:
    初始化一个Map集合,将Map集合存入到ServletContext域中.
    代码实现:
    步骤一:编写一个ServletContextListener:
    public class MyServletContextListener implements ServletContextListener{
    @Override
    /**
    监听ServletContext对象的创建的方法
    @param arg0
    /
    public void contextInitialized(ServletContextEvent sce) {
    // 创建一个Map集合:人员列表.Map的key是用户对象.Map的value是用户对应session.
    Map userMap = new LinkedHashMap();
    // 将Map集合存入到ServletContext域中.
    sce.getServletContext().setAttribute(“userMap”, userMap);
    }

@Override
/
监听ServletContext对象的销毁的方法
@param arg0
/
public void contextDestroyed(ServletContextEvent sce) {

}
}
步骤二:配置监听器:

com.juwins.listener.MyServletContextListener


* 步骤三:在User类上实现HttpSessionBindingListener
@Override
/

对象与session绑定执行的方法;
/
public void valueBound(HttpSessionBindingEvent se) {
// 获得访问用户对应session:
HttpSession session = se.getSession();
// 获得用户列表的Map集合:
ServletContext servletContext = session.getServletContext();
Map userMap = (Map) servletContext
.getAttribute(“userMap”);
// 将用户和对应session存入到map人员列表中.
userMap.put(this, session);
}

@Override
/*
对象与session解除绑定执行的方法;
/
public void valueUnbound(HttpSessionBindingEvent se) {
// 获得访问用户对应session:
HttpSession session = se.getSession();
// 获得用户列表的Map集合:
ServletContext servletContext = session.getServletContext();
Map userMap = (Map) servletContext
.getAttribute(“userMap”);
// 将用户从Map列表中移除.
userMap.remove(this);
}

步骤四:在成功页面中显示人员列表:



${ entry.key.nickname }
踢下线


1.2.3 在线踢人案例的BUG分析及解决:

问题1:用户重复登录问题:

重写User类的hashCode和equals方法
* 只要ID相同认为是同一个对象.

问题2:用同一个浏览器,先后使用不同的账号进行登录:

第二次登录用户将第一个登录的用户踢出.
第一个用户的session保存的其他的信息还是存在.

解决办法:
当用户登录的时候,将之前的session销毁.

问题3:使用不同浏览器,先后使用同一个账号进行登录:

第二个登录的用户将第一个登录的用户从map集合中踢出了!
第一个登录的用户没有在Map集合中,而且session没有销毁.导致这个用户不能被踢.

解决办法:
当用户登录的时候,先去判断用户是否已经在列表中.
* 如果在列表中:已经登录过.将登录过的用户的session取出,进行销毁.

1.2.4 在线踢人案例的踢人的功能:

  • 在人员列表上:由管理员点击踢下线链接:
    提交到Servlet:
    传递用户ID.
    从Map中获得用户对应的session.
    将session销毁.
    页面跳转.

    代码编写:
    public class KickServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    /*
    1.接收id
    2.从map列表中获得到用户对应的session.
    3.销毁session.
    4.页面跳转.
    /
    // 接收id
    Integer id = Integer.parseInt(request.getParameter(“id”));
    // 从Map中获得用户对应的session的信息.
    Map userMap = (Map) this
    .getServletContext().getAttribute(“userMap”);
    // 重写了用户对象的hashCode和equals方法.只要用户的ID相同认为是同一个对象
    User user = new User();
    user.setId(id);
    HttpSession session = userMap.get(user);
    // 销毁session;
    session.invalidate();
    // 页面跳转:
    response.sendRedirect(request.getContextPath() + “/success.jsp”);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

    doGet(request, response);
    }

    }