一、需求:顯示網站的登錄用戶和記錄登錄用戶的在線總時長以及每天登錄的時長
二、實體類設計
(1)User用戶類中設置loginTime,用於設置用戶的登錄時間(即用戶一登錄就將此屬性設置爲當前時間,用戶退出或者Session銷燬時再獲取當前時間,計算時間差),totalMinute用於記錄用戶的總在線時間
(2)UserOnlineTime類用於記錄用戶每天的在線時間
(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)用了兩天,記錄如下
id=1的用戶5.21號在線48分鐘,5.22號在線6分鐘,總在線54分鐘,正常。
(2)在線用戶顯示
五、總結:
大體實現了記錄在線的用戶,但是因爲網站用戶可以重複登錄,所以Vector中判斷了是否已經存在,如果已經存在就不放入Vector中了,如果用戶在多個瀏覽器登錄計時就不準確了。
還有如果用戶不點擊退出按鈕,而是直接關閉瀏覽器,我這裏設置Session的過期時間是5分鐘,所以也是不夠準確的。
最後就是Vector中我存放的是User對象,可以只存用戶的標識信息,然後查詢再顯示,還有一些細節需要處理。