我们不时会在日志中看到这一点:
2016-09-14 10:43:04,183 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] INFO HttpSessionListener - User Username logged out
这一行来自我们的自定义HttpSessionListener中的以下代码:
/* (non-Javadoc)
* @see javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEvent)
*/
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
UserContainer userContainer = (UserContainer) session.getAttribute(UserContainer.DEFAULT_SESSION_ATTRIBUTE);
if(userContainer != null) {
LoginView loginView = userContainer.getLoginView();
// A REST client may do a failed login after witch it has a session and a UserContainer with a null loginView.
if (loginView != null) {
String userId = loginView.getUserId();
String userName = loginView.getUserName();
// remove the user container from the UserContainerManager
UserContainerManager.getInstance().removeUserContainer(userContainer);
// log logout event
LOGGER.info(String.format("User %s - %s logged out", userId, userName));
} else {
// remove the user container from the UserContainerManager
UserContainerManager.getInstance().removeUserContainer(userContainer);
// Failed login already logged by the LoginService.
}
}
}
我们会话管理的一些背景信息:我们的会话中有一个UserContainer类,因此我们可以将所有与用户相关的信息保存在一个对象中。该对象还包含对会话的瞬态引用。
public class UserContainer implements HttpSessionBindingListener, HttpSessionActivationListener,
Serializable {
// HttpSession associated with this UserContainer :
private transient HttpSession httpSession;
/**
* The container calls this method when it is being bound to the session.
*
* @param event the event that identifies the session
*/
public void valueBound(HttpSessionBindingEvent event) {
httpSession = event.getSession();
}
/**
* Notifies the object that it is being unbound from a session. Used here
* to remove the userid from the list of the logged in users when a
* session has expired.
*
* @param e the event that identifies the session
*/
public void valueUnbound(HttpSessionBindingEvent e) {
}
/**
* Invalidate the httpSession of this user.
*/
public void invalidateHttpSession() {
httpSession.invalidate();
}
/**
* @return the httpSession for this user.
*/
public HttpSession getHttpSession() {
return httpSession;
}
}
当我们获得上述行时,被引用的用户保持连接,但大多数操作都失败了,他们必须再次登录。他们的UserContainer对象被销毁,但他们的会话仍然存在。
我个人认为这是因为他们的UserContainer中的httpSession对象正在获取GC,触发sessionDestroyed事件并销毁他们的UserContainer。然而,我的同事说,由于Java是一个ByRef语言,UserContainer中GC的httpSession对象也应该是GC的UserContainer所在的httpSession,这不会发生,因为用户不会被抛回到下次请求登录界面。
是什么导致这种情况?