javaEE SSH框架记录在线登录用户以及登录用户的在线时长

时间:2022-07-08 21:00:51

一、需求:显示网站的登录用户和记录登录用户的在线总时长以及每天登录的时长

二、实体类设计

(1)User用户类中设置loginTime,用于设置用户的登录时间(即用户一登录就将此属性设置为当前时间,用户退出或者Session销毁时再获取当前时间,计算时间差),totalMinute用于记录用户的总在线时间

(2)UserOnlineTime类用于记录用户每天的在线时间

javaEE SSH框架记录在线登录用户以及登录用户的在线时长

(3)我用的SSH框架,然后配置映射文件自动建表

三:实现

1、编写一个单例类,用于记录在线的用户

(1)单例类如下

package com.hhit.entity;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;

/**
* 单例类,记录在线的用户
* @author bob
*/
public class UserList {
private static final UserList userList = new UserList();
// //Vector是线程安全的
// //若只存id的情况--->32位机器上是32bit=4Byte,20000个用户占用20000*4/1024=78K,还有Vector,(我这里记录的是用户对象)
private Vector<User> v = new Vector<User>();

private UserList() {
}

public static UserList getInstance() {
return userList;
}

// 将用户登陆身份证保存到Vector中
public void addUser(User user) throws Exception{
try{
if(user != null){
if(v.indexOf(user)>=0)// 判断是否已经存在
return;
v.addElement(user);
}
}
catch (Exception ex) {
ex.printStackTrace();
}
finally{
}
}

// 删除用户登录ID
public void RemoveUser(User user) throws Exception{
try{
if (user != null){
// 移除用户
v.removeElement(user);
}
}
catch(Exception ex){
ex.printStackTrace();
}
finally{
}
}
//判断Vector中是否存在已经登录的用户,使用Id判断
public boolean IsExist(Integer userId) throws Exception {
try{
for (int i=0;i<v.size();i++) {
// Integer 比较
if(v.get(i).getId().equals(userId)){
return true;
}
}
return false;
}
catch (Exception ex){
ex.printStackTrace();
return false;
}
}

// 返回Vector枚举
public Enumeration getUserList() {
return v.elements();
}

// 返回迭代器
public Iterator getUserListItera() {
return v.iterator();
}

// 返回在线人数
public int getUserCount() {
return v.size();
}
}


(2)单例类中使用了Vector来记录在线的用户,我是记录用户的对象,也可以只记录用户的id,这样占用的空间就很小了,Integer在32系统中占4个字节,1万用户也只占几十Kb。

另外Vector是线程安全的,即多个线程不能同时访问Vector中的数据,只能同步来进行访问(当前在线用户的数量就是Vector的size,如果使用ArrayList,它是线程不安全的,如果多个线程同时增加和删除操作就会出现问题)


2、编写一个监听器,用于监听Session,记录用户登录的时长

(1)实现HttpSessionAttributeListener和ServletContextListener接口,实现ServletContextListener接口的主要目的是在Spring容器初始化时获取容器中的service,然后可以进行数据库的操作

@Controller
@Scope("prototype")
public class UserListener implements HttpSessionAttributeListener,ServletContextListener{

private IUserService userService;
private IUserOnlineTimeService userOnlineTimeService;

private UserList userList = UserList.getInstance();

/**
* 容器初试话加载一些需要的service
* @param sce
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
// 获取容器与相关的Service对象
ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
userService = (IUserService) ac.getBean("userServiceImpl");
userOnlineTimeService = (IUserOnlineTimeService) ac.getBean("userOnlineTimeServiceImpl");
}


(2)用户登录时,即Action中调用ActionContext.getContext().getSession().put("user", userFind);方法时,会经过HttpSessionAttributeListener监听器中的attributeAdded方法,所以可以获取User对象,并将其放到单例类中的Vector中

//设置登录的时间,----用于统计在线时长
userFind.setLoginTime(new Timestamp(new Date().getTime()));
ActionContext.getContext().getSession().put("user", userFind);
return "toIndex";


(3)用户退出操作(即ActionContext.getContext().getSession().remove())以及Session过期时会调用HttpSessionAttributeListener中attributeRemoved方法,所以可以获取当前时间,记录用户的在线时长,并存入数据库,具体的逻辑注释中有标出

/**
* ActionContext.getContext().getSession().remove()时调用
*/
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
if((event!=null)&&event.getName().equals("user")){
User userFind= (User) event.getValue();

try {
//如果Vector中有用户==》移除==》记录==>这样如果切换到别的浏览器同一账号登录且之前账号没有退出就不准确了
//如果Vector中没用户==》不记录
if(userList.IsExist(userFind.getId())){
//userList中移除User
try {
userList.RemoveUser(userFind);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("用户数量"+userList.getUserCount());
//得到当前时间
Timestamp nowTime=new Timestamp(new Date().getTime());
//计算时间差--在线时长
int durationMinute=(int)(nowTime.getTime()-userFind.getLoginTime().getTime())/(1000*60);
//得到最后一条记录
UserOnlineTime onlineTimeFind=userOnlineTimeService.findByUser(userFind);
if(onlineTimeFind==null){
onlineTimeFind=new UserOnlineTime(nowTime, durationMinute, userFind);
userOnlineTimeService.save(onlineTimeFind);
//更新总时长
userFind.setTotalMinute(durationMinute+userFind.getTotalMinute());
userService.update(userFind);
}
else{
//判断是否是当天
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
String stringNowDate= format.format(nowTime);
String stringLastDate=format.format(onlineTimeFind.getDate());
if(stringLastDate.equals(stringNowDate)){//当天的
//这次在线的时长加上已有的
durationMinute=(int)(nowTime.getTime()-userFind.getLoginTime().getTime())/(1000*60);
int realDuration=durationMinute+onlineTimeFind.getDurationMinute();
onlineTimeFind.setDate(nowTime);
onlineTimeFind.setDurationMinute(realDuration);
//更新数据库
userOnlineTimeService.update(onlineTimeFind);

//更新总时长
userFind.setTotalMinute(durationMinute+userFind.getTotalMinute());
userService.update(userFind);
}
else{//不是当天--新建
durationMinute=(int)(nowTime.getTime()-userFind.getLoginTime().getTime())/(1000*60);
onlineTimeFind=new UserOnlineTime(nowTime, durationMinute, userFind);
userOnlineTimeService.save(onlineTimeFind);
//更新总时长
userFind.setTotalMinute(durationMinute+userFind.getTotalMinute());
userService.update(userFind);
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(4)在线用户显示

//显示平台信息
public String list() throws Exception{

//在线人数
ActionContext.getContext().put("onlineCount", userList.getUserCount());
ActionContext.getContext().put("userList", userList.getUserList());
return "list";
}

四、测试:

(1)用了两天,记录如下

javaEE SSH框架记录在线登录用户以及登录用户的在线时长

javaEE SSH框架记录在线登录用户以及登录用户的在线时长

id=1的用户5.21号在线48分钟,5.22号在线6分钟,总在线54分钟,正常。

(2)在线用户显示

javaEE SSH框架记录在线登录用户以及登录用户的在线时长

五、总结:

大体实现了记录在线的用户,但是因为网站用户可以重复登录,所以Vector中判断了是否已经存在,如果已经存在就不放入Vector中了,如果用户在多个浏览器登录计时就不准确了。

还有如果用户不点击退出按钮,而是直接关闭浏览器,我这里设置Session的过期时间是5分钟,所以也是不够准确的。

最后就是Vector中我存放的是User对象,可以只存用户的标识信息,然后查询再显示,还有一些细节需要处理。