Listener
1、功能
- Servlet 2.3 中新增加的另一个功能
- 作用是监听Java Web 程序中的事件
- 对应设计模式中的Listener 模式,当事件发生的时候会自动触发该事件对应的Listener
- 主要用于对Request 、Session 、Context 等进行监控
2、listener模型
常见的监听接口
3、【跟生命周期有关的监听器】
- javax.servlet.ServletContextListener :
public void contextInitialized( ServletContextEvent event )
»当 容器 加载 ( deploy ) 一个 应用 ( "web application" ) 时 呼叫 contextInitialized
»传入的事件对象是 ServletContextEvent
public void contextDestroyed( ServletContextEvent event )
»当 容器 卸载 ( undeploy ) 一个 应用 ( "web application" ) 时 呼叫 contextDestroyed
»传入的事件对象是 ServletContextEvent
测试案例:
package ecut.listener.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class ApplicationLifecycleListener implements ServletContextListener { public ApplicationLifecycleListener() { //System.out.println( "WebApplicationLifecycleListener" ); } public void contextInitialized( ServletContextEvent sce ) { ServletContext application = sce.getServletContext(); String name = application.getServletContextName(); String contextPath = application.getContextPath(); System.out.println( "正在加载: " + name + " , 它相应的路径为: " + contextPath ); } public void contextDestroyed( ServletContextEvent sce ) { ServletContext application = sce.getServletContext(); String name = application.getServletContextName(); System.out.println( "正在卸载: " + name + " 。"); } }
<!--display-name(不是listener的子标记)标记设置ServletContextName --> <display-name>listener</display-name> <listener> <listener-class>ecut.listener.listener.ApplicationLifecycleListener</listener-class> </listener>
运行结果如下:
正在加载: listener , 它相应的路径为: /s
......................
正在卸载: listener 。
- javax.servlet.http.HttpSessionListener :
public void sessionCreated( HttpSessionEvent event )
»当 创建 会话 对象时,呼叫 sessionCreated
public void sessionDestroyed( HttpSessionEvent event )
»当 销毁 会话 对象时,呼叫 sessionDestroyed
»销毁 会话对象的时机 : session 的 有效期 到了 、调用 session.invalidate()
»可以在 web.xml 中配置 session 的有效期:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
注意,这里的有效期的时间的单位是 分钟
测试案例:
package ecut.listener.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebServlet( "/session/show" ) public class ShowSessionIdServlet extends HttpServlet { private static final long serialVersionUID = 3683961647674453725L; @Override protected void service( HttpServletRequest request , HttpServletResponse response ) throws ServletException, IOException { HttpSession session = request.getSession(); // 通过代码来设置 会话的有效期 ( 时间单位是 秒 ) session.setMaxInactiveInterval( 30 ); // 最长发呆时间 ( 从客户端最后一次访问 session 开始持续30秒 ) response.setContentType( "text/html" ); response.getWriter().println( "<h1>" + session.getId() + "</h1>" ); } }
package ecut.listener.listener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class SessionLifecycleListener implements HttpSessionListener { @Override public void sessionCreated( HttpSessionEvent event ) { HttpSession session = event.getSession(); System.out.println( "会话[ " + session.getId() + " ]创建成功" ); } @Override public void sessionDestroyed( HttpSessionEvent event ) { HttpSession session = event.getSession(); System.out.println( "准备销毁[ " + session.getId() + " ]会话" ); } }
<listener> <listener-class>ecut.listener.listener.SessionLifecycleListener</listener-class> </listener>
运行结果如下:
会话[ D2920D969A73184E4A173615758A314C ]创建成功
准备销毁[ D2920D969A73184E4A173615758A314C ]会话
- javax.servlet.ServletRequestListener :
public void requestInitialized(ServletRequestEvent event)
»当 客户端 发起请求时,即创建 请求对象 ,此时 呼叫 requestInitialized
public void requestDestroyed(ServletRequestEvent event)
»当请求 结束时 ( 响应数据已经全部返回到客户端 ) ,呼叫 requestDestroyed
测试案例:
package ecut.listener.listener; import javax.servlet.DispatcherType; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.http.HttpServletRequest; public class RequestLifecycleListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent event) { ServletRequest req = event.getServletRequest(); HttpServletRequest request = (HttpServletRequest) req ; String uri = request.getRequestURI(); DispatcherType type = request.getDispatcherType(); String name = req.getLocalName(); System.out.println( "来自"+name+"请求类型为"+type + " ,请求路径是 " + uri +"的请求结束"); } @Override public void requestInitialized(ServletRequestEvent event) { ServletRequest req = event.getServletRequest(); HttpServletRequest request = (HttpServletRequest) req ; String uri = request.getRequestURI(); DispatcherType type = request.getDispatcherType(); String name = req.getLocalName(); System.out.println( "接收到请求:来自" +name+"的请求,请求类型为"+type + " ,请求路径是 " + uri ); } }
<listener> <listener-class>ecut.listener.listener.RequestLifecycleListener</listener-class> </listener>
运行结果如下:
接收到请求:来自0:0:0:0:0:0:0:1的请求,请求类型为REQUEST ,请求路径是 /s/pages/listener/index.jsp
来自0:0:0:0:0:0:0:1请求类型为REQUEST ,请求路径是 /s/pages/listener/index.jsp的请求结束
4、【跟 属性 操作 有关的监听器】
- javax.servlet.ServletContextAttributeListener :
void attributeAdded( ServletContextAttributeEvent event )
»向 ServletContext 中添加一个不存在的 属性时 呼叫 attributeAdded
ServletContext application = request.getServletContext();
application.setAttribute( "counter" , 0 ) ; // 假设 application 中 根本就没有 counter ,这是首次添加 counter
void attributeReplaced( ServletContextAttributeEvent event )
»当 ServletContext 中已经包含 某个名称的属性时,又通过 setAttribute 来设置 设个属性值,则构成 替换操作,此时呼叫 attributeReplaced
ServletContext application = request.getServletContext();
application.setAttribute( "counter" , 1 ) ; // 替换某个属性的值时,呼叫 attributeReplaced
void attributeRemoved( ServletContextAttributeEvent event )
»当从 ServletContext 移除 某个名称的 属性时 呼叫 attributeRemoved
测试案例:
<%@ page language="java" pageEncoding="UTF-8" %> <%@ page contentType="text/html; charset=UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Listener</title> <style type="text/css"> ol li { list-style-type: lower-roman ; } .lifecycle , .application , .session { border: 1px solid #dedede ; width: 90% ; margin: 20px auto ; box-shadow: 0px 0px 5px 4px #dfdfdf ; padding: 10px 10px ; } </style> </head> <body> <div class="lifecycle"> <a href="<%= request.getContextPath() %>/session/show" >显示 Session Id </a> </div> <div class="application"> Application <form action="<%= request.getContextPath() %>/application/add" method="post" > 添加或替换属性:<input type="text" name="name" placeholder="请输入属性名"> <input type="text" name="value" placeholder="请输入属性值"> <input type="submit" value="添加" > </form> <hr> <form action="<%= request.getContextPath() %>/application/remove" method="post" > 删除属性:<input type="text" name="name" placeholder="请输入属性名"> <input type="submit" value="删除" > </form> </div> <div class="session"> Session: <form action="<%= request.getContextPath() %>/session/add" method="post" > 添加或替换属性:<input type="text" name="name" placeholder="请输入属性名"> <input type="text" name="value" placeholder="请输入属性值"> <input type="submit" value="添加" > </form> <hr> <form action="<%= request.getContextPath() %>/session/remove" method="post" > 删除属性:<input type="text" name="name" placeholder="请输入属性名"> <input type="submit" value="删除" > </form> </div> <div class="session"> <form action="<%= request.getContextPath() %>/customer/login" method="post" > <input type="text" name="username" placeholder="请输入用户名"> <input type="password" name="password" placeholder="请输入密码"> <input type="submit" value="登录" > </form> </div> </body> </html>
package ecut.listener.servlet; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet( "/application/add" ) public class ApplicationAddServlet extends HttpServlet { private static final long serialVersionUID = -1218970600318038715L; @Override protected void service( HttpServletRequest request , HttpServletResponse response ) throws ServletException, IOException { String name = request.getParameter( "name" ); String value = request.getParameter( "value" ); ServletContext application = this.getServletContext(); application.setAttribute( name , value ); // ----> ApplicationAttributeListener # attributeAdded response.sendRedirect( request.getContextPath() + "/pages/listener/index.jsp" ); } }
package ecut.listener.servlet; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet( "/application/remove" ) public class ApplicationRemoveServlet extends HttpServlet { private static final long serialVersionUID = -6802388429975749293L; @Override protected void service( HttpServletRequest request , HttpServletResponse response ) throws ServletException, IOException { String name = request.getParameter( "name" ); ServletContext application = this.getServletContext(); application.removeAttribute( name ); // ----> ApplicationAttributeListener # attributeRemoved response.sendRedirect( request.getContextPath() + "/pages/listener/index.jsp" ); } }
package ecut.listener.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeListener; public class ApplicationAttributeListener implements ServletContextAttributeListener { @Override public void attributeAdded( ServletContextAttributeEvent event ) { ServletContext application = event.getServletContext(); String name = event.getName() ; Object value = event.getValue() ; System.out.print( "向 " + application.getContextPath() + " 的 ServletContext 中添加属性: " ); System.out.println( name + " ,取值: " + value ); } @Override public void attributeReplaced( ServletContextAttributeEvent event ) { ServletContext application = event.getServletContext(); String name = event.getName() ; Object value = event.getValue() ; // 原来的值 System.out.print( application.getContextPath() + " 的 ServletContext 中的属性: " ); System.out.print( name ); System.out.print( " 的取值从 [ "); System.out.print( value ); System.out.println( " ] 替换成 [ " + application.getAttribute( name ) +" ]" ); } @Override public void attributeRemoved( ServletContextAttributeEvent event ) { ServletContext application = event.getServletContext(); String name = event.getName() ; Object value = event.getValue() ; System.out.print( "从 " + application.getContextPath() + " 的 ServletContext 中移除属性: " ); System.out.println( name + " ,取值: " + value ); } }
运行结果如下:
向 /s 的 ServletContext 中添加属性: 女朋友 ,取值: 罗玉凤
/s 的 ServletContext 中的属性: 女朋友 的取值从 [ 罗玉凤 ] 替换成 [ 芙蓉 ]
从 /s 的 ServletContext 中移除属性: 女朋友 ,取值: 芙蓉
- javax.servlet.http.HttpSessionAttributeListener : 监听 session 对象中的 属性变化的监听器
void attributeAdded( HttpSessionBindingEvent event )
void attributeReplaced( HttpSessionBindingEvent event )
void attributeRemoved( HttpSessionBindingEvent event )
测试案例:
package ecut.listener.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebServlet( "/session/add" ) public class SessionAddServlet extends HttpServlet { private static final long serialVersionUID = 7547888494941329407L; @Override protected void service( HttpServletRequest request , HttpServletResponse response ) throws ServletException, IOException { String name = request.getParameter( "name" ); String value = request.getParameter( "value" ); HttpSession session = request.getSession(); session.setAttribute( name , value ); // ----> SesssionAttributeListener # attributeAdded response.sendRedirect( request.getContextPath() + "/pages/listener/index.jsp" ); } }
package ecut.listener.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebServlet( "/session/remove" ) public class SessionRemoveServlet extends HttpServlet { private static final long serialVersionUID = -6802388429975749293L; @Override protected void service( HttpServletRequest request , HttpServletResponse response ) throws ServletException, IOException { String name = request.getParameter( "name" ); HttpSession session = request.getSession(); session.removeAttribute( name ); // ----> SesssionAttributeListener# attributeRemoved response.sendRedirect( request.getContextPath() + "/pages/listener/index.jsp" ); } }
package ecut.listener.listener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; public class SessionAttributeListener implements HttpSessionAttributeListener { @Override public void attributeAdded(HttpSessionBindingEvent event) { HttpSession session = event.getSession(); String name = event.getName() ; Object value = event.getValue() ; System.out.print( "向 会话[" + session.getId() + " ]中添加属性: " ); System.out.println( name + " ,取值: " + value ); } @Override public void attributeRemoved(HttpSessionBindingEvent event) { HttpSession session = event.getSession(); String name = event.getName() ; Object value = event.getValue() ; System.out.print( "从会话[ " + session.getId() + "] 中移除属性: " ); System.out.println( name + " ,取值: " + value ); } @Override public void attributeReplaced(HttpSessionBindingEvent event) { HttpSession session = event.getSession(); String name = event.getName() ; Object value = event.getValue() ; // 原来的值 System.out.print("会话[" + session.getId() + "] 中的属性: " ); System.out.print( name ); System.out.print( " 的取值从 [ "); System.out.print( value ); System.out.println( " ] 替换成 [ " + session.getAttribute( name ) +" ]" ); } }
运行结果如下:
向 会话[E962FCF5380CE7E04CF599D20438F25 ]中添加属性: 男朋友 ,取值: 吴彦祖
会话[E962FC9F5380CE7E04CF599D20438F25] 中的属性: 男朋友 的取值从 [ 吴彦祖 ] 替换成 [ 彭于晏 ]
从会话[ E962FC9F5380CE7E04CF599D20438F25] 中移除属性: 男朋友 ,取值: 彭于晏
- javax.servlet.ServletRequestAttributeListener : 监听 request 对象中的 属性变化的监听器
void attributeAdded( ServletRequestAttributeEvent srae )
void attributeReplaced( ServletRequestAttributeEvent srae )
void attributeRemoved(ServletRequestAttributeEvent srae)
5、【 跟 Session 钝化 和 锐化 有关的监听器 】
- javax.servlet.http.HttpSessionActivationListener :
void sessionWillPassivate( HttpSessionEvent event )
»Passivate ( 钝化 )
当 有客户端 关联的 Session 存在,并且 Session 中有数据时,如果此时 重新加载 当前Web应用,
将导致 Session 中的数据被丢失,为了保存这部分数据,容器 可能会将 Session 对象中的数据 保存到 磁盘 、网络 等存储设备中,这种行为 称作 Session 的 钝化
void sessionDidActivate( HttpSessionEvent event )
»Activate ( 锐化 )
从 硬盘或其它存储设备上的文件中重新读回 Session 数据,重新加载到 JVM 所管理的 内存中 的动作
6、【跟 属性值 绑定到 Session 有关的监听器】
- javax.servlet.http.HttpSessionBindingListener
void valueBound( HttpSessionBindingEvent event )
»如果该实例加入session 对象的属性,则呼叫valueBound()
void valueUnbound( HttpSessionBindingEvent event )
»假设 将要 添加到 session 中的 属性值 的类型是 Customer 类型
»如果该实例从session 对象种移除,则呼叫valueUnbound()
Customer c = new Customer();
session.setAttribute( "customer" , c ) ; // 将 Customer 类型的 对象 绑定到 session 的属性值中
session.removeAttribute( "customer" ) ; // 解除 Customer 类型的 对象 跟 session 的绑定
为了监听 Customer 对象 跟 session 是否 发生关系,则需要 将 Customer 本身 做成是 监听器
这个监听器无需在xml中或者是在类中使用注解注册Listener
统计用户在线人数测试案例:
package ecut.listener.entity; import java.io.Serializable; import java.util.Date; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; public class Customer implements Serializable , HttpSessionBindingListener { private static final long serialVersionUID = 7499870485797565944L; private Integer id ; private String username ; private String password ; private String nickname ; private char gender ; private Date birthdate ; @Override public void valueBound( HttpSessionBindingEvent event ) { System.out.println( "绑定到 [ " + event.getSession().getId() + " ]" ); ServletContext application = event.getSession().getServletContext(); // 1、先尝试从 ServletContext 获取名称是 loginCounter 的属性 Integer loginCounter = (Integer)application.getAttribute( "loginCounter" ); // 2、判断 loginCounter 是否不存在 if( loginCounter == null ){ loginCounter = Integer.valueOf( 0 ) ; } // 3、将 loginCounter 增加 1 以后 再 放入到 ServletContext 中 application.setAttribute( "loginCounter" , ++loginCounter ); } @Override public void valueUnbound( HttpSessionBindingEvent event ) { System.out.println( "从 [ " + event.getSession().getId() + " ] 解除绑定" ); ServletContext application = event.getSession().getServletContext(); // 1、先尝试从 ServletContext 获取名称是 loginCounter 的属性 Integer loginCounter = (Integer)application.getAttribute( "loginCounter" ); // 2、判断 loginCounter 是否不存在 if( loginCounter == null ){ application.setAttribute( "loginCounter" , 0 ); } else { // 3、将 loginCounter 减去 1 以后 再 放入到 ServletContext 中 --loginCounter; loginCounter = loginCounter < 0 ? 0 : loginCounter ; application.setAttribute( "loginCounter" , loginCounter ); } } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; } public Date getBirthdate() { return birthdate; } public void setBirthdate(Date birthdate) { this.birthdate = birthdate; } }
package ecut.listener.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import ecut.listener.entity.Customer; @WebServlet( "/customer/login" ) public class CustomerLoginServlet extends HttpServlet { private static final long serialVersionUID = 2694859066336538343L; @Override protected void service( HttpServletRequest request , HttpServletResponse response ) throws ServletException, IOException { String username = request.getParameter( "username" ); String password = request.getParameter( "password" ); Customer c = new Customer(); c.setUsername( username ); c.setPassword( password ); HttpSession session = request.getSession() ; session.setAttribute( "customer" , c ); // ---> Customer # valueBound response.sendRedirect( request.getContextPath() + "/customer/main" ); return ; } }
package ecut.listener.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebServlet( "/customer/logout" ) public class CustomerLogoutServlet extends HttpServlet { private static final long serialVersionUID = 1376263177132417940L; @Override protected void service( HttpServletRequest request , HttpServletResponse response ) throws ServletException, IOException { HttpSession session = request.getSession(); session.removeAttribute( "customer" ); // ---> Customer # valueUnbound response.sendRedirect( request.getContextPath() + "/pages/listener/index.jsp" ); } }
package ecut.listener.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import ecut.listener.entity.Customer; @WebServlet( "/customer/main" ) public class CustomerMainServlet extends HttpServlet { private static final long serialVersionUID = 154588398950897364L; @Override protected void service( HttpServletRequest request , HttpServletResponse response ) throws ServletException, IOException { response.setContentType( "text/html" ); PrintWriter w = response.getWriter(); ServletContext application = this.getServletContext() ; HttpSession session = request.getSession(); Object o = session.getAttribute( "customer" ); if( o instanceof Customer ){ Customer c = (Customer) o ; w.println( "<h1>欢迎" + c.getUsername() + " 登录</h1>" ); w.println( "<h1>当前登陆人数:" + application.getAttribute( "loginCounter" ) + " </h1>" ); w.println( "<a href='" + request.getContextPath() + "/customer/logout'>注销</a>"); } else { response.sendRedirect( request.getContextPath() + "/pages/listener/index.jsp" ); } } }
运行结果如下:
绑定到 [ E962FC9F5380CE7E04CF599D20438F25 ] 向 /s 的 ServletContext 中添加属性: loginCounter ,取值: 1 向 会话[E962FC9F5380CE7E04CF599D20438F25 ]中添加属性: customer ,取值: ecut.listener.entity.Customer@5be15f9f
绑定到 [ 2E6EF661378654D7120122FCDEEA1F88 ]
/s 的 ServletContext 中的属性: loginCounter 的取值从 [ 1 ] 替换成 [ 2 ]
向 会话[2E6EF661378654D7120122FCDEEA1F88 ]中添加属性: customer ,取值: ecut.listener.entity.Customer@2098d9
从 [ 2E6EF661378654D7120122FCDEEA1F88 ] 解除绑定
/s 的 ServletContext 中的属性: loginCounter 的取值从 [ 2 ] 替换成 [ 1 ]
从会话[ 2E6EF661378654D7120122FCDEEA1F88] 中移除属性: customer ,取值: ecut.listener.entity.Customer@2098d9
7、注册监听器
- 必须注册Listener ,容器才能为相应事件找到处理者
- 除了HttpSessionBindingListener外所有的Listener 都必须注册
- 只有注册过的Listener 容器才能进行调度
- 注册Listener
可以在web.xml 中添加相应的配置
»配置文件中,一般把Listener 的配置放在servlet 和filter 之前
Servlet 3.0 可以在监听器类中使用@WebListener 注解
»这个注解用于标注这个类是一个监听器,并注册给容器
»@WebListener 只有一个属性value,它是String 类型,指定当前Listener的描述
转载请于明显处标明出处