為何要自定義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中