• 序列化作用


    首先明白一个概念,sessio可以存储在很多位置,并不是固定在某个地方。可能是内存,也可以是硬盘,服务器关闭后,session暂时还不会失效,比如登录页面,如果服务器关闭了,session还没失效,但是开启服务器后,希望还是看到之前登录的用户登录进去的页面,这时候需要序列化改pojo对象,这样pojo对象会跟session一起保存到内存或者硬盘。

    而重新开启服务器后,session和pojo会重新被加载,原来的pojo对象也会重新显现出来。所以说session对象的序列化是为了保持对象处于一种状态。

    即序列化的作用就是:为了保存在内存中的各种对象的状态(序列化),并且可以把保存的对象状态再读出来(反序列化)。

    先简单说下:

    应用场景:

    1.一般来说,服务器启动后,就不会再关闭了,但是如果逼不得已需要重启,而用户会话还在进行相应的操作,这时就需要使用序列化将session信息保存起来放在硬盘,服务器重启后,又重新加载。这样就保证了用户信息不会丢失,实现永久化保存

    2.淘宝每年都会有定时抢购的活动,很多用户会提前登录等待,长时间不进行操作,一致保存在内存中,而到达指定时刻,几十万用户并发访问,就可能会有几十万个session,内存可能吃不消,这时就需要进行对象的活化、钝化,让其在闲置的时候离开内存,将信息保存至硬盘,等要用的时候,就重新加载进内存。

    (一理解)

    一、session的序列化和反序列化

    什么是序列化?

    把对象的状态信息转换为可以存储或传输的形式过程,简单说就是把对象转换为字节形式存储的过程称为对象的序列化

    什么是反序列化?

    把字节序列转化为对象的过程

     

    Tomcat下保存的session序列化文件


     

    实现了Serializable接口的User类

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. import java.io.Serializable;  
    2.   
    3. public class User implements Serializable {  
    4.   
    5.     private static final long serialVersionUID = 1L;  
    6.     private String username;  
    7.     private String password;  
    8.     public String getUsername() {  
    9.         return username;  
    10.     }  
    11.     public void setUsername(String username) {  
    12.         this.username = username;  
    13.     }  
    14.     public String getPassword() {  
    15.         return password;  
    16.     }  
    17.     public void setPassword(String password) {  
    18.         this.password = password;  
    19.     }  
    20.     @Override  
    21.     public String toString() {  
    22.         return "User [username=" + username + ", password=" + password + "]";  
    23.     }  
    24.     public User() {  
    25.         super();  
    26.     }  
    27.     public User(String username, String password) {  
    28.         super();  
    29.         this.username = username;  
    30.         this.password = password;  
    31.     }  
    32. }  


    向session中存放数据的a.jsp

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <%@page import="cn.cil.domain.User"%>  
    2. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
    3. <%  
    4. String path = request.getContextPath();  
    5. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  
    6. %>  
    7. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
    8. <html>  
    9.   <head>  
    10.     <base href="<%=basePath%>">  
    11.       
    12.     <title>My JSP 'a.jsp' starting page</title>  
    13.       
    14.     <meta http-equiv="pragma" content="no-cache">  
    15.     <meta http-equiv="cache-control" content="no-cache">  
    16.     <meta http-equiv="expires" content="0">      
    17.     <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">  
    18.     <meta http-equiv="description" content="This is my page">  
    19.   
    20.   </head>  
    21.     
    22.   <body>  
    23.     <h1>向 Session 存放资源</h1>  
    24.     <%  
    25.         User user = new User("123","abc");  
    26.         session.setAttribute("user",user);  
    27.     %>  
    28.   </body>  
    29. </html>  
    30. </strong></span>  

    取数据的b.jsp

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
    2. <%  
    3. String path = request.getContextPath();  
    4. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  
    5. %>  
    6.   
    7. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
    8. <html>  
    9.   <head>  
    10.     <base href="<%=basePath%>">  
    11.       
    12.     <title>My JSP 'b.jsp' starting page</title>  
    13.       
    14.     <meta http-equiv="pragma" content="no-cache">  
    15.     <meta http-equiv="cache-control" content="no-cache">  
    16.     <meta http-equiv="expires" content="0">      
    17.     <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">  
    18.     <meta http-equiv="description" content="This is my page">  
    19.   
    20.   </head>  
    21.     
    22.   <body>  
    23.     <h1>从 Seesion 中取资源</h1>  
    24.     <%= session.getAttribute("user") %>  
    25.   </body>  
    26. </html>  
    27. </strong></span>  


    在向seesion保存资源后(访问a.jsp后),关闭tomcat,然后迅速打开tomcat的work目录到指定项目文件中,就会看到生成的Sessions.ser文件,这个文件保存的就是所有session对象序列化后的信息

    这时,再重启Tomcat,会看到SESSIONS.ser文件消失,又被重新加载,再访问b.jsp,原来的对象信息还在


    模拟对象的序列化反序列化

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. import java.io.File;  
    2. import java.io.FileInputStream;  
    3. import java.io.FileOutputStream;  
    4. import java.io.ObjectInputStream;  
    5. import java.io.ObjectOutputStream;  
    6.   
    7. import org.junit.Test;  
    8.   
    9. import cn.cil.domain.User;  
    10.   
    11.   
    12. public class TestSerializable {  
    13.   
    14.     @Test  
    15.     public void test() throws Exception {  
    16.           
    17.         User user = new User("123","abc");  
    18.         SerializeObj(user);  
    19.         User users = ObjDeSerialize();  
    20.         System.out.println(users);  
    21.     }  
    22.       
    23.     private void SerializeObj(User user) throws Exception{  
    24.           
    25.         ObjectOutputStream outObj = new ObjectOutputStream(  
    26.                 new FileOutputStream(new File("G:/SESSIONS.ser")));  
    27.         outObj.writeObject(user);  
    28.         System.out.println("对象序列化完成");  
    29.     }  
    30.       
    31.     private User ObjDeSerialize() throws Exception{  
    32.         ObjectInputStream inObj = new ObjectInputStream(new FileInputStream(  
    33.                 new File("G:/SESSIONS.ser")));  
    34.         User user = (User)inObj.readObject();  
    35.         System.out.println("对象反序列化完成");  
    36.         return user;  
    37.     }  
    38.   
    39. }  



    特别注意:

    1.进行对象的序列化和反序列化的对象,必须实现Serializable接口,否则无法进行序列化和反序列化,当然这仅仅可以进行序列化和反序列化而已,如果序列化完成的对象(已经保存至硬盘),反序列化前又修改了对象,那么反序列化会失败,所以进行序列化的对象必须还要添加serialVersionUID

     

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. public class User implements Serializable{   
    2.   
    3.     //private static final long serialVersionUID = 1L; //不添加ID  
    4.     private String username;  
    5.     private String password;  
    6.     //private int age = 10; 对象序列化后,新增内容  
    7.       
    8.     public String getUsername() {  
    9.         return username;  
    10.     }  
    11.     public void setUsername(String username) {  
    12.         this.username = username;  
    13.     }  
    14.     public String getPassword() {  
    15.         return password;  
    16.     }  
    17.     public void setPassword(String password) {  
    18.         this.password = password;  
    19.     }  
    20.       
    21.   
    22.       
    23.     /*@Override 
    24.     public String toString() { 
    25.         return "User [username=" + username + ", password=" + password 
    26.                 + ", age=" + age + "]"; 
    27.     }*/  
    28.       
    29.     /*@Override 
    30.     public String toString() { 
    31.         return "User [username=" + username + ", password=" + password 
    32.                 + "]"; 
    33.     }*/  
    34.     public User() {  
    35.         super();  
    36.     }  
    37.     public User(String username, String password) {  
    38.         super();  
    39.         this.username = username;  
    40.         this.password = password;  
    41.     }  
    42. }  
    43.   
    44.   
    45. @Test  
    46.     public void test2() throws Exception {  
    47.           
    48.         User user = new User("123","abc");  
    49.           
    50.         SerializeObj(user);  
    51.           
    52.     }  
    53.     @Test  
    54.     public void test3() throws Exception {  
    55.           
    56.         User users = ObjDeSerialize();  
    57.         System.out.println(users);  
    58.           
    59.     }  
    60. </strong></span>  


    在反序列化前,打开注释部分,进行反序列化


    这也就说明了,文件流中的class和web下的classpath中的class(修改后)不一致了,会抛出异常,所以进行序列化的对象,如果需要在修改后还可以进行反序列化,就必须添加serialVersionUID,如果不指定serialVersionUID,Java编译器会自动帮我们添加,一个对象只要修改一点点,他们的serialVersionUID就会不一致,序列化前是一个serialVersionUID,反序列化又是一个serialVersionUID,两个serialVersionUID不一致,肯定会异常。Eclipse如此强大,不填写serialVersionUID时,它会报警。所以稍稍注意一下。

    2.如果一个对象不实现Serializable接口,它是无法进行序列化的,根本就无法转换为字节序列

    实现Serializable接口,就好比坐飞机的机票,有机票带你飞,没票在地上呆着


    二、session的活化和钝化

    当一个用户长时间不进行操作的时,服务器为减轻内存压力,可以将其session保存到硬盘中,等待用户再次操作的时候,再从硬盘中取出来,(保存到硬盘中的信息不会删除)

    将下面配置文件放到tomcatconfcatalinalocalhost目录下!文件名称为项目名称。

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <Context>  
    2.     <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"[如果session在1分钟内没有使用,那么Tomcat就会钝化它]>  
    3.         <Store className="org.apache.catalina.session.FileStore" directory="mysession"[把session序列化到TomcatworkCatalinalocalhostlistenermysession目录下。]/>  
    4.     </Manager>  
    5. </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:webapache-tomcat-8.0.26workCatalinalocalhostE_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就可以了,并且加上唯一标识即可。

  • 相关阅读:
    HDU4812 D Tree(树的点分治)
    BZOJ1834 [ZJOI2010]network 网络扩容(最小费用最大流)
    HDU4862 Jump(放大边权的费用流)
    SCU3185 Black and white(二分图最大点权独立集)
    HDU3729 I'm Telling the Truth(字典序最大的最大流)
    HDU3586 Information Disturbing(树形DP)
    HDU3657 Game(最小割)
    POJ3162 Walking Race(树形DP+尺取法+单调队列)
    SCU3312 Stockholm Knights(最大流)
    Codeforces 161D Distance in Tree(树的点分治)
  • 原文地址:https://www.cnblogs.com/fengli9998/p/6550227.html
Copyright © 2020-2023  润新知