- 浏览: 56847 次
- 性别:
- 来自: 上海
文章分类
最新评论
反序列化的时候,并不是调用类的构造函数来实现实例的构建,而是通过一种语言之外的对象创建机制来构造对象实例。。从底层源码来看,生成实例时调用了java.reflect.Constructor 的newInstance()方法:
// 用反射生成实例 public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { //。。。。。。此处省略。。。。。。。 return (T) constructorAccessor.newInstance(initargs); }
而最后实际上是调用了ConstructorAccessor 的newInstance()方法,而再往下,ConstructorAccessor 调用了本地方法。
由此产生了两个问题:
(一)、通过序列化可以任意创建实例,不受任何限制。那么如何确保单例类反序列化的时候满足单例特性
(二)、由于不调用类自己的构造器,可能通过构造器来保证的一些限制条件就很难满足。比如,有两个参数max,min,构造 时必须满足max>min
下面提供了解决上述两个问题的方法。
先看看单例模式是如何被序列化蹂躏的,再来看看我们解决方案。
// 单例类 public class ASingleton implements Serializable{ private static final long serialVersionUID = -4647546107954516623L; private static final ASingleton INSTANCE = new ASingleton(); private ASingleton(){ System.out.println("construct a ASingleton"); } public static ASingleton getInstance(){ return INSTANCE; } } // 单例测试类 public class TestSingleton { public static void main(String[] args) throws IOException, ClassNotFoundException{ ASingleton s = ASingleton.getInstance(); SerialUtils.writeObject(s, "asingleton.byte"); System.out.println(s); SerialUtils.readObject("asingleton.byte"); } }
测试方法会打印出序列化前对象,和反序列化后的对象,我们看看是否是同一个实例
construct a ASingleton
file size:51bytes
test.serial.resolve.ASingleton@3dfeca64
read form asingleton.byte get test.serial.resolve.ASingleton@457471e0
可以看出来不是同一个实例。这个问题的解决方案是:可以通过覆写readResolve 这个方法来实现,readResolve 会在反序列化时调用,当从流中获取信息重建对象生成实例后,将会调用这个方法,将生成的实例替换成我们想要的实例,看代码:
// 单例类覆写了readResolve public class ASingleton implements Serializable{ // 此处省略100 个字 private Object readResolve(){ System.out.println("resolve instance"); return INSTANCE;} }
可以看出,我们覆写了这个方法,并且将那个唯一的实例(INSTANCE)返回,从而保证了单例。看
看运行结果证明我们的想法:
construct a ASingleton
file size:51bytes
test.serial.resolve.ASingleton@3dfeca64
resolve instance
read form asingleton.byte get test.serial.resolve.ASingleton@3dfeca64
可以看出是一个实例,并且readResolve 也被调用了。
到底替换实例的过程是怎样的呢,我们在jdk 源码中寻求答案:
// 源码中是如何使用readResolve的 private Object readOrdinaryObject(boolean unshared) throws IOException{ // ... ObjectStreamClass desc = readClassDesc(false); desc.checkDeserialize(); Object obj; try { obj = desc.isInstantiable() ? desc.newInstance() : null; } // .... if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) { Object rep = desc.invokeReadResolve(obj); // … if (rep != obj) { handles.setObject(passHandle, obj = rep); } } return obj; }
从代码可以看出,原理是这样的:
(1)首先会从流中读取信息,重建对象obj
(2) 判断是否覆写了readResolve 方法,如果没有覆写则直接返回obj
(3) 如果覆写了readResolve 方法,则调用readResolve 方法返回实例rep,如果rep与obj不相等,则用rep 替换obj,最后返回obj替换过程就是这样的,至此,单例问题解决了。
最后看看第二个问题如何解决。第二个问题的解决思路有两个,一个是覆写readObject 并且在方法里校验我们的约束信息,如果有问题就抛出异常。代码如下:
// 利用readObject 来防攻击 public class Range implements Serializable{ // 此出省略若干字 private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException{ s.defaultReadObject(); int max = this.max; int min = this.min; if(max < min){ throw new IllegalArgumentException("max must bigger than min"); } } // …. }
另一个解决思路是让对象实现ObjectInputValidation中的public void validateObject() throws InvalidObjectException 方法。在该方法中校验反序列化之后得实例必须满足的某些特性。如不满足则抛出相应的异常。代码如下:
序列化类的代码如下:
package zheyuan.experiment4.com; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectInputValidation; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class ValidationPerson implements Serializable,ObjectInputValidation { private static final long serialVersionUID = 5497993111607600169L; private String firstName; private String lastName; private List<String> lis=new ArrayList<String>(); public ValidationPerson(){ lis.add("validationPerson"); } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public List<String> getLis() { return lis; } public void setLis(List<String> lis) { this.lis = lis; } public void validateObject() throws InvalidObjectException { //这里的这个异常是肯定会抛出来的,只是为了演示该方法的使用 if(this.lis.contains("validationPerson")){ throw new InvalidObjectException("validationPerson缺失!"); } } //注册验证对象 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.registerValidation(this, 1); in.defaultReadObject(); } }
测试类的代码如下:
package zheyuan.experiment4.com; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; public class ValidationSerializableTest { /** * 反序列化的时候通过继承实现接口ObjectInputValidation中的方法校验反序列化之后类的某些特性 * @param args */ public static void main(String[] args) throws Throwable{ ValidationPerson vp=new ValidationPerson(); //序列化 OutputStream out=new FileOutputStream("validation_sec"); ObjectOutputStream oos=new ObjectOutputStream(out); oos.writeObject(vp); //反序列化 InputStream ins=new FileInputStream("validation_sec"); ObjectInputStream ois=new ObjectInputStream(ins); ValidationPerson vpNew=(ValidationPerson)ois.readObject(); System.out.println(vpNew.getLis().get(0)); } }
运行结果如下:
Exception in thread "main" java.io.InvalidObjectException: validationPerson缺失! at zheyuan.experiment4.com.ValidationPerson.validateObject(ValidationPerson.java:50) at java.io.ObjectInputStream$ValidationList$1.run(ObjectInputStream.java:2207) at java.security.AccessController.doPrivileged(Native Method) at java.io.ObjectInputStream$ValidationList.doCallbacks(ObjectInputStream.java:2203) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:358) at zheyuan.experiment4.com.ValidationSerializableTest.main(ValidationSerializableTest.java:27)
发表评论
-
JVM参数以及调优
2011-10-24 23:39 795一、JVM配置参数中文说明: 1、-Xmixed ... -
分布式服务框架之NIO(一)
2011-10-22 23:08 1301NIO在实现分布式服务框架中非阻塞高并发的服务器端功能 ... -
Spring声明线程池配置示例
2011-09-13 14:19 723<bean id="***Ta ... -
HTTPS下载的问题
2011-08-31 17:18 1726遇到一个很恶心的问题,开发以及测试环境是http协议,下 ... -
实现自己的csv文件解析引擎
2011-08-26 14:29 1459前言: 这里仅仅支持Excel文件导出的CSV文件,解 ... -
迷茫了
2011-08-08 23:09 732又迷茫了,感觉没事可做,什么都不想做 实际并不是没事可做,实 ... -
通用的excel报表生成工具类
2011-07-20 20:41 1102下面这个工具类是今天半天的劳动成果。 以后自己也可能用得到。 ... -
正则表达式元字符总结
2011-07-14 23:22 918正则表达式元字符总结如下: 点号(.):任何单字符的通 ... -
多并发情况下日志信息中如何区分不同线程(客户端)调用
2011-07-13 21:57 1361在企业开发中,常常会遇到这样的需求:通过一个唯一标 ... -
windows下perl开发环境搭建
2011-07-09 22:07 68971、下载并安装ActivePerl,貌似不需要额外的配置,一步 ... -
如何让右键菜单出现“命令行在这里”,即cmd here
2011-06-29 00:24 4703要在命令行下跳转到某个嵌套很深的目录下时,使用cd命令等比 ... -
使用JMock简介
2011-06-28 13:36 940一、常用关键字 one ... -
利用数据库锁实现简单的防并发编程
2011-06-22 23:27 1078大约有两类情况: 1、一个程序代码块同一时刻只允许一个 ... -
程序员如何减少BUG
2011-06-21 22:04 2508最近一个项目出了大量的BUG,很是惭愧,有没有可以尽量规避BU ... -
一些常用的正则表达式(项目中经常用到)
2011-06-15 17:11 8107最近做的一个内部系统项目,涉及大量的文本校验,里面用到了一些常 ... -
解析Excel文件转换科学计数法字符串为正常数字
2011-06-14 22:55 5640问题出现的情形是这样的: excel文件中某个字段,既 ... -
js正则表达式去除表单提交字符串前后的空格
2011-06-13 19:31 1436str为表单提交数据。 str=str.replace ... -
debug容器启动类报MMO异常解决办法
2011-06-10 19:58 705类似的,在eclipse的VM arguments中设置参数: ... -
理想的设计特征
2011-06-09 00:57 695其实下面这些是来自于代码大全,觉得讲得实在是精辟无比,想摘抄下 ... -
最近的几点关于编码的心得
2011-05-25 00:35 6501、编写一个类或者新建一个变量的时候,名字问题需要仔细的斟酌斟 ...
相关推荐
这几个附录远远超出了简单的汇编语言参考向导,讲述了公共代码段(common code fragments)和常用编译器对几种典型的代码序列表现出来的编译器习性(complier idioms),并介绍了识别和破解它们的方法。
现在Python 3.8已经进入官方的beta阶段,这个版本带来了许多语法改变、内存共享、更有效的序列化和反序列化、改进的字典和更多新功能。 好了,正文开始,一起看看吧! Python 3.8 是 Python 编程语言的最新主要版本...
11.1.2 第二步:定义事件成员 11.1.3 第三步:定义负责引发事件的方法来通知事件的登记对象 11.1.4 第四步:定义方法将输入转化为期望事件 11.2 编译器如何实现事件 11.3 设计侦听事件的类型 11.4 显式实现...
24.5 控制序列化/反序列化的数据 24.5.1 如何在基类没有实现ISerializable的前提下定义一个实现它的类型 24.6 流上下文 24.7 将类型序列化为不同的类型以及将对象反序列化为不同的对象 24.8 序列化代理 24.8.1 ...
11.1.2 第二步:定义事件成员 11.1.3 第三步:定义负责引发事件的方法来通知事件的登记对象 11.1.4 第四步:定义方法将输入转化为期望事件 11.2 编译器如何实现事件 11.3 设计侦听事件的类型 11.4 显式实现...
11.1.2 第二步:定义事件成员 11.1.3 第三步:定义负责引发事件的方法来通知事件的登记对象 11.1.4 第四步:定义方法将输入转化为期望事件 11.2 编译器如何实现事件 11.3 设计侦听事件的类型 11.4 显式实现...
2.4.2 Java的对象序列化技术 98 2.4.3 Java的异常处理技术 101 2.4.4 Java线程的并发控制 104 2.4.5 Java的集合类 105 2.5 Visual C++的架构相关技术 107 2.5.1 Windows API的窗口技术与消息处理技术 107 ...
数据中台与数字化转型论坛 数据服务中台建设实践 从A最佳实践到瓴羊数据治理 3、因果推断论坛 探究产品策略优化的长期影响 序列数据的因果推断在仓储管理的应用 AIGC与因果推断的双向赋能 4、用户增长与运营论坛 ...
12.3 序列化 423 12.4 总结 426 第13章 LINQ 427 13.1 LINQ基础 427 13.1.1 延迟执行 429 13.1.2 LINQ是如何工作的 429 13.1.3 LINQ表达式 430 13.1.4 揭秘LINQ表达式 436 13.2 LINQ to DataSet ...
12.3 序列化 12.4 总结 第13章 LINQ 13.1 LINQ基础 13.1.1 延迟执行 13.1.2 LINQ是如何工作的 13.1.3 LINQ表达式 13.1.4 揭秘LINQ表达式 13.2 LINQ to DataSet 13.2.1 强类型的DataSet 13.2.2...
泛型的序列化问题 .NET 2.0 泛型在实际开发中的一次小应用 C#2.0 Singleton 的实现 .Net Framwork 强类型设计实践 通过反射调用類的方法,屬性,字段,索引器(2種方法) ASP.NET: State Server Gems 完整的动态加载/卸载...