Day61 Listener监听器
JavaWeb 三大组件(Servlet、Filter、Listener)
概念
监听器用于监听web应用中某些对象信息的创建、销毁、增加,修改,删除等动作的发生,然后作出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。
常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等。
监听器可以监听就是在
application
,session
,request
三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。
application
是ServletContext
类型的对象,ServletContext
代表整个web应用,在服务器启动的时候,tomcat会自动创建该对象。在服务器关闭时会自动销毁该对象。
创建步骤
1.创建类
2.实现指定的监听器接口中的方法
3.在web.xml文件中配置监听/在类上标注@WebListener 注解
JavaEE的监听器:
没有这个pageContext:上下文对象
1.第一大类监听器:监听请求对象的创建和销毁
监听域对象 创建与销毁的监听器
需要导包,servlet-api.jar
监听器接口 | 描述 |
---|---|
ServletRequestListener | 监听请求对象的创建、销毁 |
HttpSessionListener | 监听会话对象的创建、销毁 |
ServletContextListener | 监听全局域对象的创建、销毁 |
1.1 ServletRequestListener
请求对象的生命周期:
创建:客户端发送请求,Tomcat会创建请求对象(请求对象可以存储数据,但是时间很短,在项目中要跳转时,需要临时数据,就存储在请求对象中)
销毁:服务器返回响应,Tomcat会将请求对象销毁
MyServletRequestListener
package com.qf.listener01;
public class MyServletRequestListener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
//获取请求对象
ServletRequest request = servletRequestEvent.getServletRequest();
System.out.println("请求对象被创建了"+request);
}
@Override
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
//获取请求对象
ServletRequest request = servletRequestEvent.getServletRequest();
System.out.println("请求对象被销毁了"+request);
}
}
web.xml
也可以使用@WebListener注解
<listener>
<listener-class>com.qf.listener01.MyServletRequestListener</listener-class>
</listener>
Welcome.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Welcome.html</h1>
</body>
</html>
page01.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>page01.jsp</h1>
</body>
</html>
运行结果:
1.2 HttpSessionListener
监听会话域对象的创建和销毁
会话对象的生命周期:
创建:服务端使用到会话对象才会被创建
销毁:会话域对象过期时间到后会被销毁
注意:
1.客户端访问服务器里的html页面不会创建会话对象,因为没有使用到session
2.客户端访问服务器里的jsp页面会创建会话对象,因为使jsp里9大内置对象里有session
3.会话域对象过期时间默认30分钟
4.在web.xml中可以设置session的过期时间(时间单位:分钟),注意:最后一次使用session的时间往后移(闲置时间)
<session-config>
<session-timeout>60</session-timeout>
</session-config>
经验:
session对象在服务器中存储,默认30分钟过期时间
JSESSIONID存储在客户端的Cookie中,默认过期时间是会话结束时
这样会导致,关闭客户端后,session对象还存活在服务端,相同的客户端再次开启并发送请求访问session时就会创建新的session对象,原有的session对象就视作为占用资源的无效对象
我们可以设置JSESSIONID的过期时间(和session对象的过期时间一致),用户关闭客户端再次开启后才能找到之前在服务器对应的session对象
package com.qf.listener01;
@WebListener
public class MyHttpSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
System.out.println("会话域对象被创建了:" + session.getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
System.out.println("会话域对象被销毁了:" + session.getId());
}
}
运行结果:
Welcome.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>welcome.html</h1>
<a href="ser01">向Servlet01发送请求</a>
</body>
</html>
客户端访问servlet会不会创建session对象?
不会,只有servlet里面使用request.getSession()时,才会创建
工作流程:
1.向Tomcat获取当前客户端的session对象
2.Tomcat会获取请求里参数(cookie)的JSESSIONID
3.1 请求里参数中没有JSESSIONID,说明客户端在服务器中没有对应的session对象,就创建
3.2 请求里参数中有JSESSIONID,Tomcat通过JSESSIONID获取容器里的session对象
3.2.1 有可能没有获取到,就创建新的session对象(服务器里的session对象过期时间到了被销毁了,但是客户端Cookie里的JSESSIONID还没过期)
3.2.2 获取到,就直接取出使用
4.返回响应,响应参数里会携带JSESSIONID,为了客户端下次发送请求时还能找到之前的session对象
package com.qf.servlet;
@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
String id = session.getId();
System.out.println("Servlet01接收到请求了..." + id);
//设置JSESSIONID的过期时间(和session对象的过期时间一致)
Cookie cookie = new Cookie("JSESSIONID", id);
cookie.setMaxAge(60*30);
response.addCookie(cookie);
}
}
1.3 ServletContextListener
第一大类监听器:监听全局域对象创建和销毁
全局域对象的生命周期:
创建:项目启动时
销毁:服务器正常关闭时
经验:让整个项目共享的数据可以存储在全局域对象里
初始化数据存储在全局域对象中
package com.qf.listener01;
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
//获取全局域对象的初始化参数 -- 注意:在web.xml中配置
String code = servletContext.getInitParameter("code");
System.out.println("全局域对象被创建了:" + servletContext + " -- " + code);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
System.out.println("全局域对象被销毁了:" + servletContext );
//服务器在停止的时候,要执行某些动作,那么就可以把代码写在这个位置!!!
}
}
运行结果:
1.4 案例:统计网站在线人数
MyServletContextListener
package com.qf.listener01;
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
//设置网站在线人数
//项目启动,向application对象中存一个变量,初始值0
servletContext.setAttribute("count",0);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
MyHttpSessionListener
package com.qf.listener01;
@WebListener
public class MyHttpSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
// 有人访问了 count++
//获取全局域对象
ServletContext servletContext = session.getServletContext();
//更新网站在线人数
int count = (Integer) servletContext.getAttribute("count");
count++;
servletContext.setAttribute("count",count);
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
// 有人离开了 count--
ServletContext servletContext = session.getServletContext();
int count = (Integer) servletContext.getAttribute("count");
count--;
servletContext.setAttribute("count",count);
}
}
2.第二类:属性监听器(了解)
监听域对象属性变化的监听器
监听器接口 | 描述 |
---|---|
ServletContextAttributeListener | 监听Servlet上下文对象属性的创建、删除、替换 |
HttpSessionAttributeListener | 监听会话对象属性的创建、删除、替换 |
ServletRequestAttributeListener | 监听请求对象属性的创建、删除、替换 |
2.1 ServletRequestAttributeListener
第二大类监听器:监听请求域对象属性的添加、删除、替换
package com.qf.listener02;
@WebListener
public class MyServletRequestAttributeListener implements ServletRequestAttributeListener {
@Override
public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
ServletRequest request = servletRequestAttributeEvent.getServletRequest();
String name = servletRequestAttributeEvent.getName();
Object value = servletRequestAttributeEvent.getValue();
System.out.println(request + "请求中的属性添加了:" + name + " -- " + value);
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {
ServletRequest request = servletRequestAttributeEvent.getServletRequest();
String name = servletRequestAttributeEvent.getName();
Object value = servletRequestAttributeEvent.getValue();
System.out.println(request + "请求中的属性删除了:" + name + " -- " + value);
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {
ServletRequest request = servletRequestAttributeEvent.getServletRequest();
String name = servletRequestAttributeEvent.getName();
Object value = servletRequestAttributeEvent.getValue();//获取的被替换的value
System.out.println(request + "请求中的属性替换了:" + name + " -- " + value);
}
}
page01.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>page01.jsp</h1>
<%
request.setAttribute("msg","aaa");//添加属性
request.setAttribute("msg","bbb");//替换属性
request.removeAttribute("msg");//删除属性
%>
</body>
</html>
2.2 HttpSessionAttributeListener
第二大类监听器:监听会话对象属性的添加、删除、替换
package com.qf.listener02;
@WebListener
public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
HttpSession session = httpSessionBindingEvent.getSession();
String name = httpSessionBindingEvent.getName();
Object value = httpSessionBindingEvent.getValue();
System.out.println(session.getId() + "会话中的属性添加了:" + name + " -- " + value);
}
@Override
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
HttpSession session = httpSessionBindingEvent.getSession();
String name = httpSessionBindingEvent.getName();
Object value = httpSessionBindingEvent.getValue();
System.out.println(session.getId() + "会话中的属性删除了:" + name + " -- " + value);
}
@Override
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
HttpSession session = httpSessionBindingEvent.getSession();
String name = httpSessionBindingEvent.getName();
Object value = httpSessionBindingEvent.getValue();
System.out.println(session.getId() + "会话中的属性替换了:" + name + " -- " + value);
}
}
page01.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>page01.jsp</h1>
<%
session.setAttribute("msg","aaa");//添加属性
session.setAttribute("msg","bbb");//替换属性
session.removeAttribute("msg");//删除属性
%>
</body>
</html>
2.3 ServletContextAttributeListener
第二大类监听器:全局域对象属性的添加、删除、替换
package com.qf.listener02;
@WebListener
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
//Servlet上下文对象新增值的时候被调用
@Override
public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
ServletContext servletContext = servletContextAttributeEvent.getServletContext();
String name = servletContextAttributeEvent.getName();
Object value = servletContextAttributeEvent.getValue();
System.out.println(servletContext + "全局域中的属性添加了:" + name + " -- " + value);
}
//Servlet上下文对象删除值的时候被调用
@Override
public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
ServletContext servletContext = servletContextAttributeEvent.getServletContext();
String name = servletContextAttributeEvent.getName();
Object value = servletContextAttributeEvent.getValue();
System.out.println(servletContext + "全局域中的属性删除了:" + name + " -- " + value);
}
//Servlet上下文对象替换值的时候被调用
@Override
public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
ServletContext servletContext = servletContextAttributeEvent.getServletContext();
String name = servletContextAttributeEvent.getName();
Object value = servletContextAttributeEvent.getValue();
System.out.println(servletContext + "全局域中的属性替换了:" + name + " -- " + value);
}
}
page01.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>page01.jsp</h1>
<%
//操作全局域对象的属性
application.setAttribute("msg","aaa");//添加属性
application.setAttribute("msg","bbb");//替换属性
application.removeAttribute("msg");//删除属性
%>
</body>
</html>
3.第三类:监听HttpSession中的对象(JavaBean)重要
前两类监听器是作用在 ServletContext HttpSession ServletRequest上
第三类监听器是作用在JavaBean上的
监听器接口 | 描述 |
---|---|
HttpSessionBindingListener | 监听会话对象中JavaBean对象的绑定、删除 |
HttpSessionActivationListener | 监听会话对象中JavaBean对象的钝化、活化 |
request对象存储时间短的数据
Session存储长期的数据
监听session里面的value中的user对象的变化,第一、第二类都不能精准定位
3.1 第三大类监听器:HttpSessionBindingListener
理解:监听该类(User)对象在session中的绑定(添加)和解绑(删除)
valueBound – 绑定方法
valueUnbound – 解绑方法
package com.qf.listener03;
public class User implements HttpSessionBindingListener{
private String username;
private transient String password;
private String name;
//无参构造,有参构造,get,set,toString()方法省略
@Override
public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
HttpSession session = httpSessionBindingEvent.getSession();
String name = httpSessionBindingEvent.getName();
Object value = httpSessionBindingEvent.getValue();
System.out.println(session.getId() + "会话对象中绑定了:" + name + " -- " + value);
}
@Override
public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
HttpSession session = httpSessionBindingEvent.getSession();
String name = httpSessionBindingEvent.getName();
Object value = httpSessionBindingEvent.getValue();
System.out.println(session.getId() + "会话对象中解绑了:" + name + " -- " + value);
}
@Override
public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
System.out.println(session.getId() + "的User对象被钝化了:" + this);
}
@Override
public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
System.out.println(session.getId() + "的User对象被活化了:" + this);
}
}
3.2 第三大类监听器:HttpSessionActivationListener
理解:监听该类(User)对象在session中的钝化(序列化)和活化(反序列化)
sessionWillPassivate – 钝化(序列化):将类(User)对象写入到文件
sessionDidActivate – 活化(反序列化):将文件里的对象读取到程序中
- 钝化:当服务器正常关闭时,还存活着的session对象(在设置时间内没有销毁) 会随着服务器的关闭被以文件(“SESSIONS.ser”)的形式存储(写入到)在tomcat 的work 目录下,这个过程叫做Session 的钝化
- 活化:当服务器再次正常开启时,服务器会找到之前的“SESSIONS.ser” 文件,从中恢复之前保存起来的Session 对象,这个过程叫做Session的活化
注意:
实现session的钝化和活化底层使用到了ObjectInputStream、ObjectOutputStream
钝化和活化的JavaBean对象必须实现Serializable(序列化接口)
1.面试题:Session中的JavaBean对象什么情况下会钝化
session未过期
JavaBean对象所属的类必须实现Serializable接口
服务器正常关闭
以上三个条件必须都满足
2.面试题:Session中的JavaBean对象什么情况下不会钝化
1.session对象过期
2.session.invalidate();
3.服务器非正常关闭
以上三个条件满足一个即可
3.面试题:Session中的JavaBean对象什么情况下不会活化
服务器启动,但是session对象过期了
注意事项
- 想要Session 被钝化、活化的对象它的类必须实现Serializable 接口,还要在服务器正常关闭的条件下,还未超时的Session 才会被钝化成文件。当Session 超时、调用invalidate方法或者服务器在非正常情况下关闭时,Session 都不会被钝化,因此也就不存在活化。
- 在被钝化成“SESSIONS.ser” 文件时,不会因为超过Session 过期时间而消失,这个文件会一直存在,等到下一次服务器开启时消失。
- 当多个Session 被钝化时,这些被钝化的Session 都被保存在一个文件中,并不会为每个Session 都建立一个文件。
package com.qf.listener03;
public class User implements HttpSessionActivationListener, Serializable {
private String username;
private transient String password;
private String name;
//无参构造,有参构造,get,set,toString()方法省略
@Override
public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
System.out.println(session.getId() + "的User对象被钝化了:" + this);
}
@Override
public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
System.out.println(session.getId() + "的User对象被活化了:" + this);
}
}
在WebContent\META-INF文件夹下创建一个context.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!--
maxIdleSwap:"1": session如果1分钟没有使用就序列化
directory: 序列化后文件所保存的路径
-->
<Manager className="org.apache.catalina.session.PersistentManager"
maxIdleSwap="1">
<Store className="org.apache.catalina.session.FileStore"
directory="C:\\text" />
</Manager>
</Context>