Listener 概述
Listener(监听器)用于监听 Java Web 程序中的事件,例如创建、修改、删除 session、request、context 等,并触发相应的事件。利用 Listener 能够用很少的代码实现比较好的效果。
Listener 分类
分类
Servlet 2.5 规范中共有8种 Listener,这8种 Listener 可以分为三类:
(1) 监听 session、context、request 等的创建与销毁:HttpSessionListener、ServletContextListener、ServletRequestListener。
- HttpSessionListener:
void sessionCreated(HttpSessionEvent e)
void sessionDestroyed(HttpSessionEvent e)
- ServletContextListener:
void contextInitialized(ServletContextEvent e)
void contextDestroy(ServletContextEvent e)
- ServletRequestListener:
void requestInitialized(ServletRequestEvent e)
void requestDestroyed(ServletRequestEvent e)
(2) 监听 session、context、request 的属性变化:HttpSessionAttributeListener、ServletContextAttributeListener、ServletRequestAttributeListener。
(3) 监听 session 内的对象:HttpSessionBindingListener、HTTPSessionActivationListener
<1>HttpSessionBindingListener:当对象被放到 session 里时执行valueBound(HttpSessionBindingEvent event)
方法。当对象被从 session 里移除时执行 valueUnbound(HttpSessionBindingEvent event)
方法。对象必须实现该 Listener 接口。1>
<2>HttpSessionActivationListener:服务器关闭时,会将 session 里的内容保存到硬盘上,这个过程叫做序列化(钝化)。服务器重新启动时,会将 session 从硬盘重新加载。当 session 里的对象被序列化(钝化)时会执行sessionWillPassivate(HttpSessionEvent event)
方法,当对象被重新加载(反序列化)时执行sessionDidActivate(HttpSessionEvent event)
。对象必须实现该 Listener 接口和 Serializable 接口。2>
注意:这两种 Listener 不需要在 web.xml 中配置(当然也不需要注解配置)。。
Listener 简单例子1
监听session、context、request的创建与销毁:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package com.anye137.listener;
import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.WebListener;;
@WebListener public class ListenerTest implements HttpSessionListener, ServletContextListener, ServletRequestListener{ @Override public void contextInitialized(ServletContextEvent e) { ServletContext context = e.getServletContext(); System.out.println("启动context:"+context.getContextPath()); } @Override public void contextDestroyed(ServletContextEvent e) { ServletContext context = e.getServletContext(); System.out.println("关闭context:"+context.getContextPath()); }
@Override public void sessionCreated(HttpSessionEvent e) { HttpSession session = e.getSession(); System.out.println("创建session,id为:"+session.getId()); } @Override public void sessionDestroyed(HttpSessionEvent e) { HttpSession session = e.getSession(); System.out.println("销毁session,id为:"+session.getId()); } @Override public void requestInitialized(ServletRequestEvent e) { HttpServletRequest request = (HttpServletRequest)e.getServletRequest(); String uri = request.getRequestURI(); uri = uri+"?"+request.getQueryString(); request.setAttribute("dateCreated", System.currentTimeMillis()); System.out.println("IP "+request.getRemoteAddr()+" 请求 "+uri); } @Override public void requestDestroyed(ServletRequestEvent e) { HttpServletRequest request = (HttpServletRequest)e.getServletRequest(); long time = System.currentTimeMillis()-(Long)request.getAttribute("dateCreated"); System.out.println("IP "+request.getRemoteAddr()+"请求处理结束,用时"+time+"毫秒"); }
}
|
这个例子中的 Listener 是用注解配置的,如果用 web.xml 配置,需要添加一下代码:
1 2 3
| <listener> <listener-class>com.anye137.listener.ListenerTest</listener-class> </listener>
|
Listener 简单例子2
监听 session 属性变化(监听context、request与监听session类似)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.anye137.listener; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent;
@WebListener public class SessionAttributeListenerTest implements HttpSessionAttributeListener{
public void attributeAdded(HttpSessionBindingEvent e) { HttpSession session = e.getSession(); String name = e.getName(); System.out.println("新建session属性:"+name+",值为:"+e.getValue()); } public void attributeRemoved(HttpSessionBindingEvent e) { HttpSession session = e.getSession(); String name = e.getName(); System.out.println("删除session属性:"+name+",值为"+e.getValue()); } public void attributeReplaced(HttpSessionBindingEvent e) { HttpSession session = e.getSession(); String name = e.getName(); Object oldValue = e.getValue(); System.out.println("修改session属性:"+name+",原值:"+oldValue+",新值:"+session.getAttribute(name)); } }
|
Listener 使用案例
1.单态登录
单态登录,就是一个账号只能在一台机器上登录,如果在其他机器上登录了,则原来的登录自动失效。这样可以防止多台机器同时使用同一个账号。
LoginSessionListener.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| package com.anye137.listener; import java.util.HashMap; import javax.servlet.annotation.WebListener; import javax.servlet.http.*;
@WebListener public class LoginSessionListener implements HttpSessionAttributeListener { HashMap<String, HttpSession> map = new HashMap<>(); @Override public void attributeAdded(HttpSessionBindingEvent e){ String attrName = e.getName(); if(attrName.equals("userName")){ String attrValue = (String)e.getValue(); if(map.get(attrValue)!=null){ HttpSession session = map.get(attrValue); session.removeAttribute("userName"); } map.put(attrValue, e.getSession()); System.out.println("账号:"+attrValue+",登录"); } } @Override public void attributeRemoved(HttpSessionBindingEvent e){ String attrName = e.getName(); if(attrName.equals("userName")){ String attrValue = (String) e.getValue(); map.remove(attrValue); System.out.println("账号:"+attrValue+",注销"); } } @Override public void attributeReplaced(HttpSessionBindingEvent e){ String attrName = e.getName(); if(attrName.equals("userName")){ String oldValue = (String) e.getValue(); String newValue = (String) e.getSession().getAttribute("userName"); if(!oldValue.equals(newValue)){ if(map.get(newValue)!=null){ HttpSession session = map.get(oldValue); session.removeAttribute("userName"); map.remove(oldValue); } map.put(newValue, e.getSession()); System.out.println("原账号:"+oldValue+"。被新账号:"+newValue+"替代"); } else{ System.out.println("账号:"+oldValue+",重新登录"); } } } }
|
登录界面和处理登录的 servlet 跟前面我写过的文章里,登录验证 Filter 里的代码一样,登录后的界面则增加了每隔5s自动刷新以及推出登录的代码
登录界面login.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title></title> </head> <body> <form action="${pageContext.request.contextPath}/loginServlet" method="post"> 姓名:<input type="text" name="userName"/><br> <input type="submit" value="登录"/><br> </form> </body> </html>
|
处理登录的servlet loginServlet.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.anye137.test; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*;
@WebServlet("/loginServlet") public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.setAttribute("userName", request.getParameter("userName")); response.sendRedirect("jsp/afterLogin.jsp"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
|
登录后的界面afterLogin.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <% if(request!=null){ String action = request.getParameter("action"); if("logout".equals(action)){ out.println("jjj"); session.removeAttribute("userName"); response.sendRedirect(request.getContextPath()+"/jsp/login.jsp"); } } %> <html> <head> <title></title> <script type="text/javascript"> <!-- 每5秒刷新一次 --> setTimeout("location=location; ", 5000); </script> </head> <body> 用户名: <%= session.getAttribute("userName") %> 您已成功登陆 <br> <a href="${pageContext.request.requestURI}?action=logout">退出</a> </body> </html>
|
本例中,Listener 用 map 储存已登录的用户名及对应 session,并监听 session 中 userName 属性的变化,作出相应的处理来实现单态登录。
在实际应用中,我们也可以使用类 User 来储存用户更详细的信息,比如 ip 地址,登录时间等,这时 session 存储的属性值应该是 User 类对象。
**本文章完整代码见 [Github](https://github.com/anye137/Java-Web-Learning/tree/master/FilterAndListener)**</font>。
参考资料
(1) 《JavaWeb王者归来》