首先明白一个概念,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保存到硬盘中,等待用户再次操作的时候,再从硬盘中取出来,(保存到硬盘中的信息不会删除)
将下面配置文件放到tomcatconfcatalinalocalhost目录下!文件名称为项目名称。
- <Context>
- <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"[如果session在1分钟内没有使用,那么Tomcat就会钝化它]>
- <Store className="org.apache.catalina.session.FileStore" directory="mysession"[把session序列化到TomcatworkCatalinalocalhostlistenermysession目录下。]/>
- </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: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就可以了,并且加上唯一标识即可。