spring mvc架构,tomcat运行的web项目,需要统计系统在线用户人数。在网上找的资料,很多都是使用HttpSessionListener监听session的创建和销毁来实现,session创建则人数+1,session销毁则人数-1,人数存放在ServletContext中。但是我在使用的时候发现,用户主动退出系统(退出函数中销毁session),在线用户数不会改变,只有等session超时自动过期在线用户数才会减少。跟踪代码发现,用户主动退出系统,session销毁,但是用户主动退出系统后重定向到登录页面,session会再次建立(在线用户数-1又+1=不变)。意思就是说只要到了登录页面,不管有没有登录成功,都会建立一个session。难道tomcat在应用被访问时就创建session么?再查资料以后发现,原来tomcat并不是应用被访问就创建session,竟然也不是在动态存放内容到session中时创建,而是在调用getSession()方法时,如果session存在就访问存在的session,如果不存在就创建session并返回刚创建的session。查了代码看到项目中在登录页面时有调用getSession()做一些逻辑,所以只要进入登录页面,即便用户还没有登录,session也被创建出来了,只是还未放入用户信息等参数而已。
结论:如果能够确保session销毁后不会再使用getSession()做逻辑,那么可以使用HttpSessionListener来统计在线用户数,否则,在线人数+1的逻辑不能在监听到session创建事件时就做,可以在用户登录成功后的方法中做。代码如下:
web.xml
<listener>
<listener-class>com.demo.web.UserSessionListener</listener-class>
</listener>
UserSessionListener
package com.demo.web;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
/**
* 监听用户session以获取在线用户数
*
*
*/
public class UserSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent event) {
/**
* 人数的加法不写在这里,写在用户登录成功后
*
*/
/*System.out.println("创建了");
HttpSession session = event.getSession();// 获得Session对象
// 通过Session获得servletcontext对象
ServletContext servletContext = session.getServletContext();
Object object = servletContext.getAttribute("curUserNum");
if (object == null) {
servletContext.setAttribute("curUserNum", 1);
} else {
int num =(int)object;
servletContext.setAttribute("curUserNum", num + 1);
}*/
}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
//System.out.println("销毁了");
HttpSession session = event.getSession();// 获得Session对象
ServletContext servletContext = session.getServletContext();
/**
* 1.获取num值
* 2.减1
* 3.存入servletcontext
*
*/
Object object = servletContext.getAttribute("curUserNum");
if (object != null){
int num = (int) object;
servletContext.setAttribute("curUserNum", num - 1);
}
}
}
用户登录成功后的LoginServlet,人数增加部分:
//借助ServletContext实现,避免发生:
//用户未正常注销退出系统而是关闭页面或回到登录页(session未销毁未过期)再次登录系统,用户人数重复增加的问题
ServletContext sc = getServletContext();
//在ServletContext中获取上一次存入的该用户session信息
HttpSession oldSession = (HttpSession) sc.getAttribute(id);
//以下代码中的session为用户登录成功,把用户信息参数都放入到session后的session对象
if (oldSession != null) { //上次放入的该用户信息不为空,即表示该用户上次登录的session还未过期
if (oldSession != session) {
try {
oldSession.invalidate();//销毁
} catch (Throwable e) {
}
//把新的session放入到ServletContext中,键值为用户ID或能标识用户唯一性的参数
sc.setAttribute(id, session);
}
} else{ //用户上次登录的session已经过期或用户是首次登录
sc.setAttribute(id, session); //把该用户session放入ServletContext
/**
* 在线用户数+1
*/
Object object = sc.getAttribute("curUserNum");
if (object == null) {
sc.setAttribute("curUserNum", 1);
} else {
int num =(int)object;
sc.setAttribute("curUserNum", num + 1);
}
}
在系统注销的LoginoutServlet中,要添加session销毁操作。以及,remove掉ServletContext中登录用户的session
getServletContext().removeAttribute(Integer.toString(CurUser.getId()));
httpRequest.getSession().invalidate();
以上,完