由于这个问题的浏览量接近 5k,我认为提供一个可行的解决方案示例将是有益的。
问题中概述的方法是错误的 - 它不会处理服务器重启并且不会扩展。这是一个更好的方法。
首先,您的 HttpServlet 需要处理用户登录和注销,大致如下:
public class ExampleServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String action = req.getParameter("do");
HttpSession session = req.getSession(true);
//simple plug. Use your own controller here.
switch (action) {
case "logout":
session.removeAttribute("user");
break;
case "login":
User u = new User(session.getId(), req.getParameter("login"));
//store user on the session
session.setAttribute("user",u);
break;
}
}
}
User bean 必须是可序列化的,并且必须在反序列化时重新注册自己:
class User implements Serializable {
private String sessionId;
private String login;
User(String sessionId, String login) {
this.sessionId = sessionId;
this.login = login;
}
public String getLogin() { return login; }
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
//re-register this user in sessions
UserAttributeListener.sessions.put(sessionId,this);
}
}
您还需要一个 HttpAttributeListener 来正确处理会话生命周期:
public class UserAttributeListener implements HttpSessionAttributeListener {
static Map<String, User> sessions = new ConcurrentHashMap<>();
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
if ("user".equals(event.getName()))
sessions.put(event.getSession().getId(), (User) event.getValue());
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
if ("user".equals(event.getName()))
ExampleServlet.sessions.remove(event.getSession().getId());
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
if ("user".equals(event.getName()))
ExampleServlet.sessions.put(event.getSession().getId(),
(User)event.getValue());
}
}
当然,你需要在 web.xml 中注册你的监听器:
<listener>
<listener-class>com.example.servlet.UserAttributeListener</listener-class>
</listener>
之后,您可以随时访问 UserAttributeListener 中的静态映射,以了解正在运行的会话数、每个用户正在使用的会话数等。理想情况下,您将拥有一个更复杂的数据结构,以保证其独立具有适当访问方法的单例类。使用写时复制并发策略的容器也可能是一个好主意,具体取决于用例。