JavaWeb有三大组件,Servlet、Listener、Filter。本文将介绍Listener,主要将从用处、分类、使用方法等进行介绍。
一、用处
监听器是一个专门用于对其他对象身上发生的事件或状态改变进行监听和采取相应处理的对象,当被监听的对象发生情况时,立即采取相应的行动。实际上监听器是一个实现了某一特定接口的java类,该类专门用于监听另一个Java对象的方法调用或属性改变。通过重写实现接口中定义的方法,用于当被监听对象发生某一方法调用或属性改变事件后,该方法立即被执行。
补图
二、分类
2.1 常用的Web事件监听器接口有如下几个:
监听器接口 | 用处 |
---|---|
ServletContextListener | 监听Web应用的启动或关闭 |
ServletContextAttributeListener | 监听ServletContext范围内属性的改变 |
ServletRequestListener | 监听用户请求 |
ServletRequestAttributeListener | 监听ServletRequest范围内属性的改变 |
HttpSessionListener | 监听用户session的开始和结束 |
HttpSessionAttributeListener | 监听HttpSession范围内属性的改变 |
此外,还有另外两个监听器,Servlet规范定义了8个监听器,HttpSessionBindingListener、HttpSessionActiveationListener。
2.2 按监听的事件源分类
可分为3类,分别为监听ServletContext、HttpSession和ServletRequest三个域对象。
2.3 按监听范围分类
- 域对象生命周期的监听:ServletContextListener、HttpSessionListener、ServletRequestListener。
- 域对象属性监听:ServletContextAttributeListener、HttpSessionAttribute-Listener、ServletRequestAttributeListener。
- 感知监听(都与HttpSession域对象有关):监听绑定到HttpSession域中的某个JavaBean对象的状态的监听器,HttpSessionBindingListener、HttpSessionActiveationListener。
三、接口方法及使用方法介绍
使用Listener只需要两个步骤:
- 定义Listener实现类
- 通过注解(@WebListener)或在web.xml文件中配置Listener
3.1 ServletContextListener接口包含两个方法
- contextInitialized(ServletContextEvent eve):启动Web应用时,系统调用Listener该方法。
- contextDestroyed(ServletContextEvent eve):关闭Web应用时,系统调用Listener该方法。
/**
* ServletContextListener
*/
@WebListener
public class GetConnectionListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
/**
* 这里用的是《轻量级JavaEE》书中的例子,将一个数据库连接设置成application属性,
* 需要在web.xml文件中配置context-param。但这种做法性能非常差,只当做例子看看
* ServletContextListener的用法。
*/
try {
ServletContext application = servletContextEvent.getServletContext();
String driver = application.getInitParameter("driver");
String url = application.getInitParameter("url");
String user = application.getInitParameter("user");
String password = application.getInitParameter("password");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
application.setAttribute("connection",connection);
} catch (ClassNotFoundException e) {
throw new RuntimeException("未找到类");
} catch (SQLException e) {
throw new RuntimeException("Listener中获取数据库连接出错!");
}
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
ServletContext application = servletContextEvent.getServletContext();
Connection connection = (Connection) application.getAttribute("connection");
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException("数据库连接关闭出错。");
}
}
}
}
3.2 ServletContextAttributeListener接口包含三个方法
- attributeAdded(ServletContextAttributeEvent event): 当程序把一个属性存入application范围时触发该方法。
- attributeRemoved(ServletContextAttributeEvent event): 当程序把一个属性从application范围删除时触发该方法。
- attributeReplaced(ServletContextAttributeEvent event): 当程序替换application范围内的属性时将触发该方法。
/**
* ServletContextAttributeListener
*/
@WebListener
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
ServletContext application = servletContextAttributeEvent.getServletContext();
String name = servletContextAttributeEvent.getName();
Object value = servletContextAttributeEvent.getValue();
System.out.println(application + "范围内添加了名为"
+ name + ",值为“" + value +"”的属性!");
}
@Override
public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
ServletContext application = servletContextAttributeEvent.getServletContext();
String name = servletContextAttributeEvent.getName();
Object value = servletContextAttributeEvent.getValue();
System.out.println("name = "+name+", value = “"+value+"”的属性被删除了!");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
ServletContext application = servletContextAttributeEvent.getServletContext();
String name = servletContextAttributeEvent.getName();
Object value = servletContextAttributeEvent.getValue();
System.out.println("name = "+name+", value = “"+value+"”的属性被修改了!");
}
}
3.3 ServletRequestListener接口包含两个方法
- requestInitialized(ServletRequestEvent event): 用户请求到达、被初始化时触发该方法。
- requestDestroyed(ServletRequestEvent event): 用户请求到达、被销毁时触发该方法。
3.4 ServletRequestAttributeListener接口包含三个方法
- attributeAdded(ServletRequestAttributeEvent event)
- attributeRemoved(ServletRequestAttributeEvent event)
- attributeReplaced(ServletRequestAttributeEvent event)
这三个方法与ServletContextAttributeListener的相似,不再重复介绍。
3.5 HttpSessionListener
- sessionCreated(HttpSessionEvent event): 用户与服务器的会话开始、创建时触发该方法。
- sessionDestroyed(HttpSessionEvent event): 用户与服务器的会话断开、销毁时触发该方法。
/**
* HttpSessionListener
* 通过该监听器监听系统的在线用户
*/
@WebListener
public class OnlineListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
ServletContext application = session.getServletContext();
String sessionId = session.getId();
/**
* 新建一个会话,获取全文属性“online”,
* 将用户添加到“online”列表中。
*/
if (session.isNew()) {
String user = (String) session.getAttribute("user");
user = (user == null)?"游客":user;
Map<String, String> online = (Map<String, String>) application.getAttribute("online");
if (online == null) {
online = new Hashtable<>();
}
online.put(sessionId, user);
application.setAttribute("online", online);
}
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
/**
* 会话销毁时,将"online"列表中对应的session移除。
*/
HttpSession session = httpSessionEvent.getSession();
ServletContext application = session.getServletContext();
String sessionId = session.getId();
Map<String, String> online = (Map<String, String>) application.getAttribute("online");
if (online != null) {
online.remove(sessionId);
}
application.setAttribute("online", online);
}
}
3.6 HttpSessionAttributeListener包含三个方法
- attributeAdded(HttpSessionBindingEvent event)
- attributeRemoved(HttpSessionBindingEvent event)
- attributeReplaced(HttpSessionBindingEvent event)
与ServletContextAttributeListener相类似,这里不再做介绍。
需要说明的是,应用程序完成可以采用一个监听器来监听多个事件,只要让该监听器类实现多个监听器接口即可。