首先明白一個概念,sessio可以存儲在很多位置,並不是固定在某個地方。可能是內存,也可以是硬盤,服務器關閉後,session暫時還不會失效,比如登錄頁面,如果服務器關閉了,session還沒失效,但是開啓服務器後,希望還是看到之前登錄的用戶登錄進去的頁面,這時候需要序列化改pojo對象,這樣pojo對象會跟session一起保存到內存或者硬盤。
而重新開啓服務器後,session和pojo會重新被加載,原來的pojo對象也會重新顯現出來。所以說session對象的序列化是爲了保持對象處於一種狀態。
即序列化的作用就是:爲了保存在內存中的各種對象的狀態(序列化),並且可以把保存的對象狀態再讀出來(反序列化)。
先簡單說下:
應用場景:
1.一般來說,服務器啓動後,就不會再關閉了,但是如果逼不得已需要重啓,而用戶會話還在進行相應的操作,這時就需要使用序列化將session信息保存起來放在硬盤,服務器重啓後,又重新加載。這樣就保證了用戶信息不會丟失,實現永久化保存
2.淘寶每年都會有定時搶購的活動,很多用戶會提前登錄等待,長時間不進行操作,一致保存在內存中,而到達指定時刻,幾十萬用戶併發訪問,就可能會有幾十萬個session,內存可能吃不消,這時就需要進行對象的活化、鈍化,讓其在閒置的時候離開內存,將信息保存至硬盤,等要用的時候,就重新加載進內存。
(一理解)
一、session的序列化和反序列化
什麼是序列化?
把對象的狀態信息轉換爲可以存儲或傳輸的形式過程,簡單說就是把對象轉換爲字節形式存儲的過程稱爲對象的序列化
什麼是反序列化?
把字節序列轉化爲對象的過程
Tomcat下保存的session序列化文件
實現了Serializable接口的User類
- import java.io.Serializable;
- public class User implements Serializable {
- private static final long serialVersionUID = 1L;
- private String username;
- private String password;
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- @Override
- public String toString() {
- return "User [username=" + username + ", password=" + password + "]";
- }
- public User() {
- super();
- }
- public User(String username, String password) {
- super();
- this.username = username;
- this.password = password;
- }
- }
向session中存放數據的a.jsp
- <%@page import="cn.cil.domain.User"%>
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <base href="<%=basePath%>">
- <title>My JSP 'a.jsp' starting page</title>
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- <meta http-equiv="expires" content="0">
- <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
- <meta http-equiv="description" content="This is my page">
- </head>
- <body>
- <h1>向 Session 存放資源</h1>
- <%
- User user = new User("123","abc");
- session.setAttribute("user",user);
- %>
- </body>
- </html>
- </strong></span>
取數據的b.jsp
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <base href="<%=basePath%>">
- <title>My JSP 'b.jsp' starting page</title>
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- <meta http-equiv="expires" content="0">
- <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
- <meta http-equiv="description" content="This is my page">
- </head>
- <body>
- <h1>從 Seesion 中取資源</h1>
- <%= session.getAttribute("user") %>
- </body>
- </html>
- </strong></span>
在向seesion保存資源後(訪問a.jsp後),關閉tomcat,然後迅速打開tomcat的work目錄到指定項目文件中,就會看到生成的Sessions.ser文件,這個文件保存的就是所有session對象序列化後的信息
這時,再重啓Tomcat,會看到SESSIONS.ser文件消失,又被重新加載,再訪問b.jsp,原來的對象信息還在
模擬對象的序列化反序列化
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import org.junit.Test;
- import cn.cil.domain.User;
- public class TestSerializable {
- @Test
- public void test() throws Exception {
- User user = new User("123","abc");
- SerializeObj(user);
- User users = ObjDeSerialize();
- System.out.println(users);
- }
- private void SerializeObj(User user) throws Exception{
- ObjectOutputStream outObj = new ObjectOutputStream(
- new FileOutputStream(new File("G:/SESSIONS.ser")));
- outObj.writeObject(user);
- System.out.println("對象序列化完成");
- }
- private User ObjDeSerialize() throws Exception{
- ObjectInputStream inObj = new ObjectInputStream(new FileInputStream(
- new File("G:/SESSIONS.ser")));
- User user = (User)inObj.readObject();
- System.out.println("對象反序列化完成");
- return user;
- }
- }
特別注意:
1.進行對象的序列化和反序列化的對象,必須實現Serializable接口,否則無法進行序列化和反序列化,當然這僅僅可以進行序列化和反序列化而已,如果序列化完成的對象(已經保存至硬盤),反序列化前又修改了對象,那麼反序列化會失敗,所以進行序列化的對象必須還要添加serialVersionUID
- public class User implements Serializable{
- //private static final long serialVersionUID = 1L; //不添加ID
- private String username;
- private String password;
- //private int age = 10; 對象序列化後,新增內容
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- /*@Override
- public String toString() {
- return "User [username=" + username + ", password=" + password
- + ", age=" + age + "]";
- }*/
- /*@Override
- public String toString() {
- return "User [username=" + username + ", password=" + password
- + "]";
- }*/
- public User() {
- super();
- }
- public User(String username, String password) {
- super();
- this.username = username;
- this.password = password;
- }
- }
- @Test
- public void test2() throws Exception {
- User user = new User("123","abc");
- SerializeObj(user);
- }
- @Test
- public void test3() throws Exception {
- User users = ObjDeSerialize();
- System.out.println(users);
- }
- </strong></span>
在反序列化前,打開註釋部分,進行反序列化
這也就說明了,文件流中的class和web下的classpath中的class(修改後)不一致了,會拋出異常,所以進行序列化的對象,如果需要在修改後還可以進行反序列化,就必須添加serialVersionUID,如果不指定serialVersionUID,Java編譯器會自動幫我們添加,一個對象只要修改一點點,他們的serialVersionUID就會不一致,序列化前是一個serialVersionUID,反序列化又是一個serialVersionUID,兩個serialVersionUID不一致,肯定會異常。Eclipse如此強大,不填寫serialVersionUID時,它會報警。所以稍稍注意一下。
2.如果一個對象不實現Serializable接口,它是無法進行序列化的,根本就無法轉換爲字節序列
實現Serializable接口,就好比坐飛機的機票,有機票帶你飛,沒票在地上呆着
二、session的活化和鈍化
當一個用戶長時間不進行操作的時,服務器爲減輕內存壓力,可以將其session保存到硬盤中,等待用戶再次操作的時候,再從硬盤中取出來,(保存到硬盤中的信息不會刪除)
將下面配置文件放到tomcat\conf\catalina\localhost目錄下!文件名稱爲項目名稱。
- <Context>
- <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"[如果session在1分鐘內沒有使用,那麼Tomcat就會鈍化它]>
- <Store className="org.apache.catalina.session.FileStore" directory="mysession"[把session序列化到Tomcat\work\Catalina\localhost\listener\mysession目錄下。]/>
- </Manager>
- </Context>
當然也可以放到tomcat的config下的context.xml中,這樣就是對Tomcat下所有應用都生效
session數據
(二理解)
序列化是什麼?
簡單說就是爲了保存在內存中的各種對象的狀態,並且可以把保存的對象狀態再讀出來。雖然你可以用你自己的各種各樣的方法來保存Object States,但是Java給你提供一種應該比你自己好的保存對象狀態的機制,那就是序列化。
2、什麼情況下需要序列化
a)當你想把的內存中的對象保存到一個文件中或者數據庫中時候;
b)當你想用套接字在網絡上傳送對象的時候;
c)當你想通過RMI傳輸對象的時候;
3、當對一個對象實現序列化時,究竟發生了什麼?
在沒有序列化前,每個保存在堆(Heap)中的對象都有相應的狀態(state),即實例變量(instance ariable)比如:
Foo myFoo = new Foo();
myFoo .setWidth(37);
myFoo.setHeight(70);
當通過下面的代碼序列化之後,MyFoo對象中的width和Height實例變量的值(37,70)都被保存到foo.ser文件中,這樣以後又可以把它 從文件中讀出來,重新在堆中創建原來的對象。當然保存時候不僅僅是保存對象的實例變量的值,JVM還要保存一些小量信息,比如類的類型等以便恢復原來的對 象。
服務器重啓後,要想得到session的原來對象的的方法:
衆所周知,session是服務器端的一種會話技術,只要session沒有關閉,一個會話就會保持。這裏先引出一個問題:如果我在訪問某個頁面後,服務器重啓了一下,但是網頁還沒關,那麼原來的session還在麼?答案是很明顯的,你都把服務器關掉了,session肯定不是原來的session了,原來的像登錄信息等一些跟session相關的信息肯定就沒了。但是如果我們想要服務器重啓後,還是原來的session,那跟如何做呢?
我們看下session的存儲方式:
1.1保存在IIS進程中:
保存在IIS進程中是指把Session數據保存在IIS的運行的進程中,也就是inetinfo.exe這個進程中,這也是默認的Session的存方式,也是最常用的。
這種方式的優點是簡單,性能最高。但是當重啓IIS服務器時Session丟失。
1.2.保存在StateServer上
這種存儲模式是指將Session數據存儲在一個稱爲Asp.Net狀態服務進程中,該進程獨立於Asp.Net輔助進程或IIS應用程序池的單獨進程,使用此模式可以確保在重新啓動Web應用程序時保留會話狀態,並使會話狀態可以用於網絡中的多個Web服務器。可以通過序列化到內存中,保存session對象。
序列化是將內存中的二進制取出來,session數據存儲在內存中,如果實現序列化的話,可以讀取出來,即關閉服務器後開了之後還可以獲取到原來的session對象。
1.3.保存在SQL Server數據庫中
可以配置把Session數據存儲到SQL Server數據庫中,爲了進行這樣的配置,程序員首先需要準備SQL Server數據服務器,然後在運行.NET自帶安裝工具安裝狀態數據庫。
這種方式在服務器掛掉重啓後都還在,因爲他存儲在內存和磁盤中。
這就涉及到了一個叫序列化(Serializable)的技術。當對象存儲到硬盤的時候,就需要實現序列化接口,序列化的功能就是添加了一個唯一的ID(類主鍵),這樣在反序列化(從硬盤加載到內存)的時候就可以成功找到相應的對象。另外,還要弄清楚一件事情:一般大家都覺得容器關閉後,session就銷燬了,其實不是這樣的,容器的關閉並不會導致session的銷燬。過程是這樣子的,一旦容器關閉後,session就會被持久化到硬盤,並沒有真正銷燬,爲了說明這個問題,來做個試驗:打開tomcat的工作目錄下正在運行的工程目錄:我的是E:\web\apache-tomcat-8.0.26\work\Catalina\localhost\E_shop,裏面只有一個org的文件夾,其他什麼也沒有,現在我們重啓tomcat服務器,注意觀察這裏面的變化,當服務器停掉後,這個該目錄下多了個SESSION.ser文件,服務器重啓成功後,該文件又消失了。如下:
所以,如果項目中的POJO實現了Serializable接口,當反序列化的時候就能找到剛剛序列化時候的POJO,原來session中的內容就能成功反序列化,session還是原來的session,這樣原來頁面的東西還在,刷新後還是繼續上次的操作。如果POJO沒有被實例化,那麼在session發序列化的時候當然就沒有了這些POJO了。下面看一下我的項目中的部分POJO,如下:
最後總結一下:
1. 容器關閉後session並沒有消失,而是被持久化到了硬盤裏;
2. 如果項目中的POJO實現了Serializable接口,那麼會跟着session一起被持久化到硬盤,在反序列化的時候會成功還原;
3. 要想服務器重啓後,還是原來的session,還繼續緊接着原來的頁面操作的話,就需要序列化項目中的POJO。
6、相關注意事項
a)當一個父類實現序列化,子類自動實現序列化,不需要顯式實現Serializable接口;
b)當一個對象的實例變量引用其他對象,序列化該對象時也把引用對象進行序列化;
c)並非所有的對象都可以序列化,,至於爲什麼不可以,有很多原因了,比如:
1.安全方面的原因,比如一個對象擁有private,public等field,對於一個要傳輸的對象,比如寫到文件,或者進行rmi傳輸 等等,在序列化進行傳輸的過程中,這個對象的private等域是不受保護的。
2. 資源分配方面的原因,比如socket,thread類,如果可以序列化,進行傳輸或者保存,也無法對他們進行重新的資源分 配,而且,也是沒有必要這樣實現。
因爲session是用來傳輸各種值和對象的 而對象不能通過網絡直接傳輸 所以必須序列化,
如果不序列化的話,可能導致網絡中傳輸失敗.
序列化的方法很簡單,只要實現Java.io.serialia就可以了,並且加上唯一標識即可。