1. 程式人生 > >會話管理之session技術

會話管理之session技術

        上一節我們總結了cookie技術,這節主要總結一下session技術。

1. session物件

        在web開發中,伺服器可以為每個使用者瀏覽器建立一個會話物件(session物件),注意:一個瀏覽器獨佔一個session物件(預設情況下)。因此,在需要儲存使用者資料時,伺服器程式可以把使用者資料寫到使用者瀏覽器獨佔的session中,當用戶使用瀏覽器訪問其它程式時,其它程式可以從使用者的session中取出該使用者的資料,為使用者服務。
        session和cookie的主要區別在於:cookie是把使用者的資料寫給使用者的瀏覽器(儲存在客戶機);session技術把使用者的資料寫到使用者獨佔的session中(儲存在伺服器)。
        session物件由伺服器建立,開發人員可以呼叫request物件的getSession方法得到session物件。

2. session實現原理

        瀏覽器A第一次訪問Servlet1,伺服器會建立一個session,每個session都有一個id號,建立好了後,伺服器將id號以cookie的形式回送給客戶機(這些是伺服器自動完成的)。當瀏覽器未關閉前再次發請求訪問Servlet2時,就會帶著這個id號去訪問伺服器,這時候伺服器檢索下記憶體中有沒有與之對應的session,有就用這個session為其服務。
        如果想要關掉瀏覽器再開啟還可以使用同一個session,則需要給伺服器回送的cookie設定有效時間(伺服器自動回送的時候是沒有有效期的)。具體做法是通過session物件的getId方法獲得該session的id,然後建立一個cookie,該cookie的名字為"JSESSIONID",值就是剛剛獲得的id,再將該cookie設定下有效期,(也可以設定下Path),並新增到cookie中即可。但是有效期不得超過30分鐘,因為瀏覽器關掉後,session只儲存30分鐘。

        下面通過一個案例來說明一下session的使用。

3. session的一個案例

        通過三個servlet來實現簡單的購物功能:

        IndexServlet顯示首頁,並列出所有書(這個servlet和上一節cookie的案例中基本一致)

//首頁:列出所有書
public class IndexServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		response.setContentType("text/html;charset=UTF-8");	
		PrintWriter out = response.getWriter();
		
		out.write("本網站有如下書:<br/>");
		
		Set<Map.Entry<String,Book>> set = DB.getAll().entrySet();
		for(Map.Entry<String, Book> me : set) {
			Book book = me.getValue();
			out.write(book.getName() + "<a href='/test/servlet/BuyServlet?id="+book.getId()+"'>購買</a><br/>");
			
		}

	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}

//寫一個類來模擬資料庫
class DB {
	
	private static Map<String,Book> map = new LinkedHashMap();
	
	//靜態程式碼塊中的內容只執行一次,該類在載入時,往map集合中put一系列書,map也需要設定為靜態的
	static{
		
		map.put("1", new Book("1", "javaweb開發","老張", "一本好書"));
		map.put("2", new Book("2", "spring開發","老倪", "一本好書"));
		map.put("3", new Book("3", "hibernate開發","老童", "一本好書"));
		map.put("4", new Book("4", "struts開發","老畢", "一本好書"));
		map.put("5", new Book("5", "ajax開發","老張", "一本好書"));
		map.put("6", new Book("6", "java基礎","老孫", "一本好書"));
		
	}
	
	public static Map getAll() {
		return map;
	}	
}

class Book {
	
	private String id;
	private String name;
	private String author;
	private String description;
				
	public Book() {
	}

	public Book(String id, String name, String author, String description) {
		super();
		this.id = id;
		this.name = name;
		this.author = author;
		this.description = description;
	}
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}		
}
         從上面程式中可以看出,當用戶點選購買時,將書的id號帶上,並跳轉到BuyServlet去處理:
public class BuyServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		String id = request.getParameter("id"); //獲得url中帶過來的引數id
		Book book = (Book)DB.getAll().get(id); //在DB中獲得該id號的book
		
		HttpSession session = request.getSession(); //獲得當前session物件
		Cookie cookie = new Cookie("JSESSIONID", session.getId()); //設定新的cookie,注意cookie名必須為JSESSIONID,值為該session的id
		cookie.setMaxAge(30*60); //設定cookie有效期
		cookie.setPath("/test"); //設定cookie的路徑
		response.addCookie(cookie); //將cookie新增到cookies中帶給瀏覽器,下次瀏覽器訪問,就會將此cookie帶過來了
		
		//先把書加到容器裡,再把容器加到session中。一般先檢查使用者的session中有沒有儲存書的容器,沒有就建立,有就加
		List list = (List)session.getAttribute("list");
		if(list == null) {
			list = new ArrayList();
			session.setAttribute("list", list);
		}
		list.add(book);
		
		//跳轉到顯示使用者買過哪些商品
//		request.getRequestDispatcher("/servlet/ListCartServlet").forward(request, response);
		response.sendRedirect("/test/servlet/ListCartServlet");

	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}
}
       從上面的程式可以看出,當用戶點選購買後,會將書的id號帶過來,我們拿到id號後就可以找到相應的書,同時我們將當前session的id儲存到cookie中,再帶給瀏覽器,這樣下次瀏覽器訪問的時候就會將當前session的id帶過來了。拿到相應的書後,放到list中,再把list放到session中,這樣下次跳轉的時候,瀏覽器帶來的cookie中有當前session的id,我們可以通過getSession()獲得當前的session,再把session中儲存的list拿出來,就知道使用者買了哪些書了。這就是購物車的原理,請看下面的ListCartServlet:
public class ListCartServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		response.setContentType("text/html;charset=UTF-8");	
		PrintWriter out = response.getWriter();
		
		HttpSession session = request.getSession(); //獲得當前的session
		List<Book> list = (List)session.getAttribute("list"); //從session中拿出list
		
		if(list == null || list.size() == 0) {
			out.write("對不起,您還沒有購買任何商品!");
			return;
		}
		
		out.write("您買過如下商品:<br/>");
		for(Book book : list) {
			out.write(book.getName() + "<br/>");
		}

	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}
}

4. 瀏覽器禁用cookie後的session處理

        由上文可知,session通過向瀏覽器回送cookie,如果使用者將瀏覽器的cookie禁用了該如何解決?
        解決方案:url重寫,讓session的id不以cookie的形式帶過來,以url中帶過來。有兩個url重寫的方法:

response.encodeRedirectURL(java.lang.String url);  //用於對sendRedirect方法後的url地址進行重寫。
response.encodeURL(java.lang.String url);  //用於對錶單action和超連結的url地址進行重寫。
在BuyServlet.java中,把session的id號寫入cookie的幾行程式碼去掉,將
response.sendRedirect("/test/servlet/ListCartServlet");
改為:
response.sendRedirect(response.encodeRedirectURL("/test/servlet/ListCartServlet"));
在IndexServlet.java中,把
out.write(book.getName() + "<a href='/test/servlet/BuyServlet?id="+book.getId()+"'>購買</a><br/>");
改寫成:
String url= "/test/servlet/BuyServlet?id=" + book.getId();
url= response.encodeURL(url);
out.write(book.getName() + "<a href='" + url + "'>購買</a><br/>");
還有最後一步很重要:在
out.write("本網站有如下書:<br/>");
之前加上
request.getSession();
因為要實現共享一個session,必須首先獲得session。

這樣就算瀏覽器禁用了cookie,我們依然可以共享一個session了。

5. 總結

        1. 伺服器是如何做到一個session為一個瀏覽器的多次請求而服務的?
        伺服器建立session出來後,會把session的id號以cookie的形式回寫給客戶機,這樣,只要客戶機的瀏覽器不關,再去訪問伺服器時,都會帶著session的id號去,伺服器發現客戶機帶session的id過來了,就會使用記憶體中與之對應的session為之服務。
        2. 如何做到一個session為多個瀏覽器服務?
        伺服器第一次建立session,程式設計師把session的id號手動以cookie的形式回送給瀏覽器,並設定cookie的有效期,這樣即使使用者的瀏覽器關了,開新的瀏覽器時,還會帶著session的id號找伺服器,伺服器從而就可以用記憶體中與之對應的session為第二個瀏覽器視窗服務。
        3. 如何做到使用者禁用cookie後,session還能為多次請求而服務?
       把使用者可能點選的每一個超連結後面,都跟上使用者的session id號。
        4. session物件的建立和銷燬時機
        使用者第一次request.getSession時建立。session物件預設在30分鐘沒有使用,則伺服器會自動銷燬session。但是使用者可以在web.xml檔案中手動配置session的失效時間:  下面表示1分鐘失效:

<session-config>
        <session-timeout>1</session-timeout>
</session-config>
        使用者也可以手動呼叫session.invalidate方法,摧毀session。

       session就總結這麼多,如有錯誤之處,歡迎留言指正~

_____________________________________________________________________________________________________________________________________________________

-----樂於分享,共同進步!