為何要自定義session掃描器
由於服務器來管理session的銷毀不怎麼靠譜,因此很多網站都會自己定義一個session掃描器來管理session的創建和銷毀。
實現思路
首先,創建一個session掃描器類SessionScanner,然後繼承HttpSessionListener,在sessionCreated方法中,獲取session,這個時候我們需要創建一個容器,用來存放session,然後繼承ServletContextListener,在contextInitialized方法(在web應用啟動時執行此方法)中定義一個定時器,將一段時間內沒有用到的session銷毀。
代碼實現如下:
1 package com.ccfdod.web.listener;
2
3 import java.util.Collections;
4 import java.util.Iterator;
5 import java.util.LinkedList;
6 import java.util.List;
7 import java.util.ListIterator;
8 import java.util.Timer;
9 import java.util.TimerTask;
10
11 import javax.servlet.ServletContextEvent;
12 import javax.servlet.ServletContextListener;
13 import javax.servlet.http.HttpSession;
14 import javax.servlet.http.HttpSessionEvent;
15 import javax.servlet.http.HttpSessionListener;
16
17 public class SessionScanner implements HttpSessionListener,ServletContextListener {
18
19 //這裡使用Collections.synchronizedList(List<T> list)是考慮到線程並發問題,因此將容器設置為線程安全的
20 //Collections為集合的幫助類,如發現具體的集合類不能提供某些功能,可查看該幫助類的方法,看是否存在該功能
21 //另外使用LinkedList而不使用ArrayList的原因是ArrayList底層為數據,用來做增刪改查不適合,效率較低
22 private List<HttpSession> list = Collections.synchronizedList(new LinkedList<HttpSession>());
23
24 //定義一個鎖對象,用於解決線程並發時,session的添加和除去若同時進行,會引起定時器中對list的session操作時,產生迭代器並發修改異常
25 private Object lock = new Object();
26 public void contextInitialized(ServletContextEvent sce) {
27 //這裡使用Timer作為定時器,當然也可以使用其他的
28 Timer timer = new Timer();
29 timer.schedule(new MyTask(list,lock), 0, 30*1000);
30 }
31
32 public void sessionCreated(HttpSessionEvent se) {
33 //第一次調用getSession時,服務器創建一個session
34 //但在jsp中,默認調用request.getSession()方法,創建一個session
35 //但可以在jsp文件的第一行,添加session="false",這樣在訪問jsp頁面時,就不會創建session了
36 HttpSession session = se.getSession();
37 System.out.println(session + "被創建了!!");
38 synchronized (lock) { //鎖旗標
39 list.add(session);
40 }
41 }
42 public void sessionDestroyed(HttpSessionEvent se) {
43 System.out.println(se.getSession() + "被銷毀了");
44 }
45
46 public void contextDestroyed(ServletContextEvent sce) {
47
48 }
49 }
50
51 class MyTask extends TimerTask{
52 private List list;
53 private Object lock;
54 public MyTask(List list,Object lock){
55 this.list = list;
56 this.lock = lock;
57 }
58 @Override
59 public void run() {
60 System.out.println("定時器執行!!");
61 synchronized (this.lock) {
62 //ListIterator為Iterator的子類,提供了對crud操作
63 ListIterator it = list.listIterator();
64 while(it.hasNext()){
65 HttpSession session = (HttpSession) it.next();
66 if((System.currentTimeMillis()-session.getLastAccessedTime())>30*1000){
67 session.invalidate();
68 //list.remove(session); //迭代器並發修改異常
69 //若對集合迭代,需要對集合進行crud操作時,使用迭代器的方法對集合進行crud
70 it.remove();
71 }
72 }
73 }
74 }
75 }
最後
將SessionScanner添加至web.xml中