在Servlet/JSP中除了ServletContextListener
外,还有ServletRequestListener
、HttpSessionListener
等监听器,可以监听请求、会话对象生命周期;ServletRequestAttributeListener
、HttpSessionAttributeListener
、ServletContextAttributeListener
可以监听属性的添加、删除或替换;HttpSessionBindingListener
、HttpSessionActivationListener
可以监听会话属性对象的绑定、删除。
所谓的生命周期监听器,就是可以在某个对象生成到被容器销毁之前,监听对象生命周期的状态变化。如在应用程序初始化或结束前,会分别调用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中,有三个对象可以设置属性,分别是ServletContext
、HttpSession
和ServletRequest
。如果想要在这些对象被设置、删除或替换属性时,收到通知以进行一些动作,则可以实现相对应的ServletContextAttributeListener
、HttpSessionAttributeListener
和ServletRequestAttributeListener
接口。
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
中进行设置,这里所要介绍的HttpSessionBindingListener
与HttpSessionActivationListener
,则是让即将加入HttpSession
的属性对象实现,HttpSession
在适当的时机就会直接调用接口上对应的方法,不必在web.xml
中做任何设置。
如果有个即将加入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
定义了两个方法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()
方法中做某些恢复该成员状态的动作。