一、案例一:自定义Session扫描器
1、案例说明当一个Web应用创建的Session很多时,为了避免Session占用太多的内存,我们可以选择手动将这些内存中的session销毁,那么此时也可以借助监听器技术来实现。对于拿到 每个session 对象, 判断session的最后一次访问时间 与当前时间 的间隔是否超过 5 分钟, 如果超过就手动销毁2、实现代码
SessionScanner:session对象的监听器MyTimerTask:定时器timer的任务对象
SessionScanner监听器,使用servlet3.0新特性,使用注解@WebListener完成注册
(1)SessionScanner类
package sessionScanner;import java.util.Collections;import java.util.LinkedList;import java.util.List;import java.util.Timer;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.annotation.WebListener;import javax.servlet.http.HttpSession;import javax.servlet.http.HttpSessionEvent;import javax.servlet.http.HttpSessionListener;/* * 自定义session扫描器的实现 * 手动的 自己去管理 session 对象, 如果某个session,5分钟没有被访问过,那么就销毁 * * * 1、监听session对象的修改时间,要设置监听器:HttpSessionListener(session产生和销毁时) * 注意:定义一个容器,用来装session对象,在session产生时,添加到容器中; * 在session销毁时,从容器中移除;由此进行管理 * 2、设置一个定时器(timer),每隔5分钟进行检查一次,看看哪些session是超过5分钟没有被访问过, * 如果没有,则销毁 * 3、定时器timer有个任务对象,我们需要自己去创建这个任务对象:遍历检查session中超过5分钟的情况 * 而这个任务对象(TimerTask)是Runnable接口的实现类,所以只需实现Runnable的接口即可 * 4、定时器的启动需要进行设置,因此还要设置一个监听器:ServletContextListener; * 目的:设置监听器,web应用启动时开始工作,然后每隔5分钟检查一次 * *小结: * 1. 增删频繁的时候, 使用 LinkedList 性能好 * 2. 如何将一个list 变为一个线程安全的list,使用Collections.synchronizedList * 3. 定时器的使用 --- Timer 类 * 4. 遍历list集合的时候, 同时还要从list中去移除 元素, 避免 并发修改的异常(用ListIterator,而不是Iterator) * 5. 如何实现两段不同的代码 互斥,锁的使用 *//* * * (1)实现HttpSessionListener监听器,实现两个方法 * public void sessionCreated(HttpSessionEvent se) {} * public void sessionDestroyed(HttpSessionEvent se) {} * * (2)实现ServletContextListener监听器,实现两个方法 * public void contextInitialized(ServletContextEvent sce) {} * public void contextDestroyed(ServletContextEvent sce) {} *///监听器(观察者)@WebListenerpublic class SessionScanner implements HttpSessionListener, ServletContextListener {// 自定义session扫描器的实现 // 定义一个容器, 将 每次 创建的session 对象放到 容器中去 // private List<HttpSession> list=new LinkedList<HttpSession>();//线程不安全 private List<HttpSession> list = Collections .synchronizedList(new LinkedList<HttpSession>());// 线程安全 public Object lock = new Object();// 定义一个锁,用于解决线程安全问题 // 主要解决:本对象的sessionCreated方法添加session,而MyTimerTask对象中方法销毁对象时,使用的是同一个session容器, // 这样,对同一个容器做不同的操作,肯能产生线程安全问题,所以要定义锁:lock,去解决这个线程安全问题 @Override // 事件对象(封装 session事件源 ) public void sessionCreated(HttpSessionEvent se) { System.out.println("执行了,说明新创建了一个session对象。。。"); HttpSession session = se.getSession();// 获取事件对象 // 定义锁,解决线程安全问题 synchronized (lock) { list.add(session);// 穿件的session放到容器中去 } // long lastAccessedTime = session.getLastAccessedTime();//最后一次修改时间 } @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("执行了,说明销毁了一个session对象。。。"); } @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("contextInitialized.............."); // 定义一个定时器,并且在web应用启动时开始工作 Timer timer = new Timer(); // 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行 // task:安排的任务 // delay:举例开始的指定的延时时间 // period:重复时间 //立刻 启动 定时器, 每隔 5 分钟 重复 执行 timer.schedule(new MyTimerTask(list,lock), 0, 1000 * 60 * 5);// 1000毫秒*60*5=5分钟 } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("contextDestroyed......"); }}(2)MyTimerTask类package sessionScanner;import java.util.List;import java.util.ListIterator;import java.util.TimerTask;import javax.servlet.http.HttpSession;//任务对象class MyTimerTask extends TimerTask { private List<HttpSession> list;// session容器 private Object lock;//锁,从SessionScanner中 传递而来 public MyTimerTask(List<HttpSession> list,Object lock) { this.list = list;// 获取session容器 this.lock=lock;//锁,从SessionScanner中 传递而来 } @Override public void run() { // 定义锁,解决线程安全问题 synchronized (lock) { // 遍历session容器 ListIterator<HttpSession> it = list.listIterator(); while (it.hasNext()) { HttpSession session = it.next(); // 遍历 list , 拿到 每个session 对象, 判断session的最后一次访问时间 与当前时间 的间隔是否超过 5 分钟, 如果超过就手动销毁 if (session.getLastAccessedTime() - 1000 * 60 * 5 > 0) { session.invalidate();// 销毁session list.remove(session);// 从session容器中销毁session对象 } } } }}二、案例二:统计在线用户人数及用户信息1、案例说明 统计在线用户人数及用户信息1、在线用户的数量
2、在线用户的信息:sessionId,ip地址,上一次访问时间
做法:
1、统计用户数量,通过HttpSessionListener完成 并且定义number来存储数量(通过ServletContext设置,获取)
2、统计用户信息,通过ServletRequestListener完成
并且定义List容器来存储所有用户信息(通过ServletContext设置,获取)
备注:使用时,需要自己打开不同的浏览器,才会有测试效果
2、实现代码
User:用户的数据封装对象Count:统计在线用户数量
CountInfo:统计在线用户信息
Util:工具类,用于判断所有用户中,是否存在当前用户的访问信息
index.jsp页面:在线用户人数和在线用户信息显示
(1)User
package countUser;/* * 统计在线用户人数及用户信息 * 1、在线用户的数量 * 2、在线用户的信息:sessionId,ip地址,上一次访问时间 * * 做法: * 1、统计用户数量,通过HttpSessionListener完成 --定义number来存储数量(通过ServletContext设置,获取) * 2、统计用户信息,通过ServletRequestListener完成 --定义List容器来存储所有用户信息(通过ServletContext设置,获取) * * 备注:使用时,需要自己打开不同的浏览器,才会有测试效果 */public class User { private String sessionId; private String ip; private String firstTime; public String getSessionId() { return sessionId; } public void setSessionId(String sessionId) { this.sessionId = sessionId; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getFirstTime() { return firstTime; } public void setFirstTime(String firstTime) { this.firstTime = firstTime; }}(2)Countpackage countUser;import java.util.ArrayList;import javax.servlet.annotation.WebListener;import javax.servlet.http.HttpSessionEvent;import javax.servlet.http.HttpSessionListener;//Servlet3.0 新特性//1、统计用户数量,通过HttpSessionListener完成 --定义number来存储数量(通过ServletContext设置,获取)@WebListenerpublic class Count implements HttpSessionListener { private int num=0;//统计用户在线人数 @Override public void sessionCreated(HttpSessionEvent se) { num++; //设置到ServletContext域中 se.getSession().getServletContext().setAttribute("num",num); System.out.println(" add...."); } @Override public void sessionDestroyed(HttpSessionEvent se) { num--; //设置到ServletContext域中 se.getSession().getServletContext().setAttribute("num",num); System.out.println(" remove...."); //注意,此处还要进行设置 //从session列表中移除session ArrayList<User> list=null; list=(ArrayList<User>) se.getSession().getServletContext().getAttribute("list"); if(Util.getByUserId(list, se.getSession().getId())!=null){ list.remove(Util.getByUserId(list, se.getSession().getId())); } }}(3)CountInfopackage countUser;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import javax.servlet.ServletRequestEvent;import javax.servlet.ServletRequestListener;import javax.servlet.annotation.WebListener;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;//2、统计用户信息,通过ServletRequestListener完成 --定义List容器来存储所有用户信息(通过ServletContext设置,获取)@WebListenerpublic class CountImfo implements ServletRequestListener { private ArrayList<User> list;//存储访问用户的信息 @Override public void requestInitialized(ServletRequestEvent sre) { //获得ServletContext域中的list list=(ArrayList<User>) sre.getServletContext().getAttribute("list");// if(list.isEmpty()) if(list==null){ list=new ArrayList<User>(); } //为了获得session对象,进行强制类型转换 HttpServletRequest request=(HttpServletRequest)sre.getServletRequest(); HttpSession session = request.getSession(); String sessionId = session.getId();//sessionId //session的列表中没有当前的sessionId,即:以前的所有访问用户中,没有当前的访问用户,所以把当前的访问用户信息加入 if(Util.getByUserId(list,sessionId)==null){ User user=new User(); user.setSessionId(sessionId); user.setIp(request.getRemoteAddr()); //设置时间 user.setFirstTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));// user.setFirstTime(System.currentTimeMillis()+""); list.add(user); }// request.getServletContext().setAttribute("list", list);//保存所有用户信息到list中 sre.getServletContext().setAttribute("list", list);//保存所有用户信息到list中 } @Override public void requestDestroyed(ServletRequestEvent sre) { }}(4)Util package countUser;import java.util.ArrayList;public class Util { //用于判断所有用户中,是否存在当前用户的访问信息 public static Object getByUserId(ArrayList<User> list, String sessionId) { // if(!list.isEmpty()){ for(int i=0;i<list.size();i++){ User user = list.get(i); if(user.getSessionId().equals(sessionId)){ return user;//所有用户中有,已经存在当前用户的访问信息 } }// } return null;//所有用户中有,不存在当前用户的访问信息 }}(5)index.jsp页面<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head></head><body>
当前在线用户人数:${num }<br/><!-- 当前用户在线人数: ${num}<br /> -->所有访问者的信息列表<br/> <c:if test="${ empty list}"> 当前不存在访问者 </c:if> <c:if test="${ not empty list}"> <c:forEach var="user" items="${list }"> sessionId:${user.sessionId } ip:${user.ip } firstTime:${user.firstTime } <br/> </c:forEach> </c:if><!-- <br/>遍历方式二:<br/> <% ArrayList<countUser.User> userList = (ArrayList<countUser.User>)request.getServletContext().getAttribute("list"); if(userList!=null){ for(int i = 0 ; i < userList.size() ; i++){ countUser.User user = userList.get(i); %> IP:<%=user.getIp() %>,FirstTime:<%=user.getFirstTime() %>,SessionId:<%=user.getSessionId() %> <br/> <%}} %>--></body></html>3、实现结果