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域对象的创建和销毁.
方法:
监听ServletContext对象创建的方法:
监听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中进行配置即可.
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域对象创建和销毁.
方法:
监听HttpSession对象创建的方法:
监听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中完成配置
HttpSessionListener的作用:
进行session扫描.
** 记录网站的在线人数! setAttribute()**
ServletRequestListener:监听ServletRequest域对象创建和销毁.
方法:
监听ServletRequest对象创建的方法:
监听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对象创建了…”);
}
}
* 步骤二:对监听器进行配置:
1.1.4 二类:监听三个域对象的属性变更的监听器.(属性添加,属性移除,属性替换).
ServletContextAttributeListener:监听ServletContext中的属性变更.
方法:
监听ServletContext对象中的属性添加的方法:
监听ServletContext对象中的属性移除的方法:
* 监听ServletContext对象中的属性替换的方法:
HttpSessionAttributeListener:监听HttpSession中的属性变更.
方法:
监听HttpSession对象中属性添加的方法:
监听HttpSession对象中属性移除的方法:
* 监听HttpSession对象中属性替换的方法:
ServletRequestAttributeListener:监听ServletRequest中的属性变更.
方法:
监听ServletRequest对象中的属性添加的方法:
监听ServletRequest对象中的属性移除的方法:
* 监听ServletRequest对象中的属性替换的方法:
1.1.5 三类:监听HttpSession中对象的状态变化的监听器.(绑定,解除绑定,钝化,活化)
三类监听器特殊的特性:
1.监听HttpSession的对象.
监听向session中存java对象.
2.这类监听器不需要进行配置:
需要让JavaBean自己感知在session中状态.
HttpSessionBindingListener:监听HttpSession中的JavaBean的状态(绑定和解除绑定状态)
方法:
监听HttpSession中JavaBean的绑定的状态.
监听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的状态(钝化和活化状态)
钝化:将对象序列化到硬盘.
活化:将硬盘对象反序列化过程.
方法:
监听HttpSession中JavaBean的钝化的方法:
监听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的路径中.
—>
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.
MapuserMap = new LinkedHashMap ();
// 将Map集合存入到ServletContext域中.
sce.getServletContext().setAttribute(“userMap”, userMap);
}
@Override
/
监听ServletContext对象的销毁的方法
@param arg0
/
public void contextDestroyed(ServletContextEvent sce) {
}
}
步骤二:配置监听器:
* 步骤三:在User类上实现HttpSessionBindingListener
@Override
/
对象与session绑定执行的方法;
/
public void valueBound(HttpSessionBindingEvent se) {
// 获得访问用户对应session:
HttpSession session = se.getSession();
// 获得用户列表的Map集合:
ServletContext servletContext = session.getServletContext();
Map
.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
.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的信息.
MapuserMap = (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);
}
}