[Servlet&JSP] 监听器的使用

时间:2022-08-04 21:06:44

在Servlet/JSP中除了ServletContextListener外,还有ServletRequestListenerHttpSessionListener等监听器,可以监听请求、会话对象生命周期;ServletRequestAttributeListenerHttpSessionAttributeListenerServletContextAttributeListener可以监听属性的添加、删除或替换;HttpSessionBindingListenerHttpSessionActivationListener可以监听会话属性对象的绑定、删除。

生命周期监听器

所谓的生命周期监听器,就是可以在某个对象生成到被容器销毁之前,监听对象生命周期的状态变化。如在应用程序初始化或结束前,会分别调用contextInitialized()contextDestroyed()方法,通过传入的ServletContextEvent取得ServletContext,以针对应用程序作出相对应的初始化或结束处理。

  • ServletContextListener
    • contextInitialized(sce: ServletContextEvent): void
    • contextDestroyed(sce: ServletContextEvent): void
  • ServletContextEvent
    • ServletContextEvent(source: ServletContext)
    • getServletContext(): ServletContext

如果想要在ServletRequest(HttpServletRequest)对象生成或结束时,做些相应动作,则可以实现ServletRequestListener

  • ServletRequestListener
    • requestInitialized(sre: ServletRequestEvent): void
    • requestDestroyed(sce: ServletRequestEvent): void
  • ServletRequestEvent
    • ServletRequestEvent(sc: ServletContext, request: ServletRequest)
    • getServletRequest(): ServletRequest
    • getServletContext(): ServletContext

如果想要在HttpSession对象生成或结束时,做些相应动作,则可以实现HttpSessionListener

  • HttpSessionListener
    • sessionCreated(sre: HttpSessionEvent): void
    • sessionDestroyed(sce: HttpSessionEvent): void
  • HttpSessionEvent
    • HttpSessionEvent(source: HttpSession)
    • getSession(): HttpSession

一个HttpSessionListener应用实例如下,假设有个应用程序在用户登录时会使用HttpSession对象来进行会话管理:

//...
public class Login extends HttpServlet{
//...
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("abc".equals(username) && "123".equals(password)) {
request.getSession().setAttribute("login", username);
request.getRequestDispatcher("user.jsp")
.forward(request, response);
} else {
response.sendRedirect("login.html");
}
}
}

这个Servlet在用户登录验证后,会取得HttpSession实例并设置属性。如果想要在应用程序中,加上显示目前已登录在线人数的功能,则可以实现HttpSessionListener接口。例如:

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class LoginListener implements HttpSessionListener {
private static int count;

public static int getCount() {
return count;
}

@Override
public void sessionCreated(HttpSessionEvent se) {
count++;
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
count--;
}
}

为了使用这个监听器,要在web.xml中加以设置

<web-app ...>
<listener>
<listener-class>club.chuxing.LoginListener</listener-class>
</listener>
</web-app>

要显示在线人数时,使用LoginListener.getCount()即可。

属性操作监听器

在Servlet/JSP中,有三个对象可以设置属性,分别是ServletContextHttpSessionServletRequest。如果想要在这些对象被设置、删除或替换属性时,收到通知以进行一些动作,则可以实现相对应的ServletContextAttributeListenerHttpSessionAttributeListenerServletRequestAttributeListener接口。

  • ServletContextAttributeListener
    • attributeAdded(scab: ServletContextAttributeEvent): void
    • attributeRemoved(scab: ServletContextAttributeEvent): void
    • attributeReplaced(scab: ServletContextAttributeEvent): void
  • HttpSessionAttributeListener
    • attributeAdded(se: HttpSessionBindingEvent): void
    • attributeRemoved(se: HttpSessionBindingEvent): void
    • attributeReplaced(se: HttpSessionBindingEvent): void
  • ServletRequestAttributeListener
    • attributeAdded(srae: ServletRequestAttributeEvent): void
    • attributeRemoved(srae: ServletRequestAttributeEvent): void
    • attributeReplaced(srae: ServletRequestAttributeEvent): void

当在这三个对象中加入、删除或替换属性时,相对应的方法就会被调用。如果要使用这几个监听器,必须同样在web.xml中使用<listener><listener-class>进行设置。

会话属性类监听器

前面几个监听器都必须在web.xml中进行设置,这里所要介绍的HttpSessionBindingListenerHttpSessionActivationListener,则是让即将加入HttpSession的属性对象实现,HttpSession在适当的时机就会直接调用接口上对应的方法,不必在web.xml中做任何设置。

HttpSessionBindingListener

如果有个即将加入HttpSession的属性对象,希望在设置给HttpSession成为属性或从HttpSession中删除时,可以收到HttpSession的通知,则可以让该对象实现HttpSessionBindingListener接口。

当属性对象被加入到HttpSession中或从中删除时,就会调用对应的valueBound()valueUnbound()方法。

  • HttpSessionBindingListener
    • valueBound(event: HttpSessionBindingEvent): void
    • valueUnbound(event: HttpSessionBindingEvent): void

接下来是该接口的一个范例,假设有登录程序如下:

//...
public class Login extends HttpServlet{
//...
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("abc".equals(username) && "123".equals(password)) {
User user = new User(name);
request.getSession().setAttribute("login", user);
request.getRequestDispatcher("user.jsp")
.forward(request, response);
} else {
response.sendRedirect("login.html");
}
}
}

当用户输入正确的用户名和密码时,首先会以用户名称来创建User实例,而后加入HttpSession中作为属性。当希望User实例被加入成为HttpSession属性时,可以自动从数据库中加载用户的其他数据,比如地址、相片等,并在日志中记录用户登录的信息;而当HttpSession失效或者因用户注销而User实例从HttpSession中删除时,则在日志中记录用户注销的信息。在这种情况下,可以让User类实现HttpSessionBindingListener接口。例如:

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

public class User implements HttpSessionBindingListener {
private String name;
private String otherData;

public User(String name) {
this.name = name;
}

@Override
public void valueBound(HttpSessionBindingEvent event) {
this.otherData = name + ": query data from database...";
Logger.getLogger(User.class.getName()).log(Level.SEVERE, this.name + " login...", event);
}

@Override
public void valueUnbound(HttpSessionBindingEvent event) {
Logger.getLogger(User.class.getName()).log(Level.SEVERE, this.name + " logout...", event);
}

//Getter and Setter
}

HttpSessionActivationListener

HttpSessionActivationListener定义了两个方法sessionWillPassivate()sessionDidActivate()

  • HttpSessionActivationListener
    • sessionWillPassivate(se: HttpSessionEvent): void
    • sessionDidActivate(se: HttpSessionEvent): void

大部分情况下,几乎不会使用到HttpSessionActivationListener。在使用到分布式环境时,应用程序的兑现可能分散在多个JVM中。当HttpSession要从一个JVM迁移至另一个JVM时,必须现在原本的JVM上序列化所有的属性对象。在这之前若属性对象实现了HttpSessionActivationListener,就会调用sessionWillPassivate()方法。当HttpSession迁移至另一个JVM后,就会对所有对象做反序列化,此时就会调用sessionDidActivate()方法。

要实现序列化的对象必须实现Serializable接口,如果在HttpSession对象中,有些类别成员无法做序列化,则可以在sessionWillPassivate()方法中做些替代处理来保存该成员的状态,而在sessionDidActivate()方法中做某些恢复该成员状态的动作。