前言
前几天, 看 R大 的一篇文章的时候, 发现了一篇有趣的文章 : https://rednaxelafx.iteye.com/blog/850082
关于这篇文章的重点 反序列化 来创建对象的细节, 我也还是想了解了解
我之前以为的就是 序列化就是 先把对象的相关元数据 以及 相关的需要存储的数据信息 序列华为字节序列, 然后 反序列化的时候 读取相关元数据信息, 创建给定的 对象, 然后 在读取序列化的数据相关信息, set 到对象的相关字段上面, 然后 完成了 对象 到 字节序列的相互转换
但是 R大 这么一说, 感觉 这个反序列化的过程 好像不是这么简单啊
更多更高深的理解, 请参见 R大 的文章
以下内容 相当于是 读了 R大 的文章的一些读后需要记录的东西, 以便于加深印象吧
以下代码基于 : jdk 1.7_40
测试代码
import com.hx.test01.Test21Unsafe;
import sun.misc.Unsafe;
import java.io.*;
/**
* Test07UnsafeAllocate
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2019/6/8 10:48
*/
public class Test07UnsafeAllocate {
// Test07UnsafeAllocate
public static void main(String[] args) throws Exception {
Unsafe unsafe = Test21Unsafe.getUnsafe();
// 1. create an object do not call constructor
// Person p1 = new Person();
Person p1 = new Person("xx", "12");
Person p2 = (Person) unsafe.allocateInstance(Person.class);
// 2. object deserialize
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(buffer);
oos.writeObject(p1);
byte[] byteTransfered = buffer.toByteArray();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteTransfered));
Object p3 = ois.readObject();
System.out.println("end ...");
}
/**
* Person
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2019/6/8 10:52
*/
private static class Person extends Creature implements Serializable {
/**
* attrs
*/
private String name;
private String age;
// public Person() {
// name = "xx";
// age = "12";
// System.out.println("person created ");
// }
public Person(String name, String age) {
this.name = name;
this.age = age;
System.out.println("person created ");
}
}
/**
* Creature
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2019/6/8 11:22
*/
private static class Creature {
public Creature() {
System.out.println("creature created ");
}
}
}
以上程序运行结果如下
creature created
person created
creature created
end ...
p1 调用 Person 的构造方法, 打印了前两句
p2 没有调用构造方法, 没有输出
p3 调用的 Creature 的构造方法, 打印了第三句
以上代码, 主要有两个部分
1. 通过 unsafe. allocateInstance 来创建给定的对象, 而不初始化
2. 具体的反序列化来创建给定的对象
通过 unsafe. allocateInstance 来创建给定的对象, 而不初始化
第一部分 是使用 Unsafe 的相关 api 来创建给一个对象, 但是并不初始化
这部分 对应的代码 应该是
unsafe.cpp
UNSAFE_ENTRY(jobject, Unsafe_AllocateInstance(JNIEnv *env, jobject unsafe, jclass cls))
UnsafeWrapper("Unsafe_AllocateInstance");
{
ThreadToNativeFromVM ttnfv(thread);
return env->AllocObject(cls);
}
UNSAFE_END
jni.cpp
JNI_ENTRY(jobject, jni_AllocObject(JNIEnv *env, jclass clazz))
JNIWrapper("AllocObject");
#ifndef USDT2
DTRACE_PROBE2(hotspot_jni, AllocObject__entry, env, clazz);
#else /* USDT2 */
HOTSPOT_JNI_ALLOCOBJECT_ENTRY(
env, clazz);
#endif /* USDT2 */
jobject ret = NULL;
DT_RETURN_MARK(AllocObject, jobject, (const jobject&)ret);
instanceOop i = alloc_object(clazz, CHECK_NULL);
ret = JNIHandles::make_local(env, i);
return ret;
JNI_END
static instanceOop alloc_object(jclass clazz, TRAPS) {
KlassHandle k(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz)));
Klass::cast(k())->check_valid_for_instantiation(false, CHECK_NULL);
instanceKlass::cast(k())->initialize(CHECK_NULL);
instanceOop ih = instanceKlass::cast(k())->allocate_instance(THREAD);
return ih;
}
这部分 就不说细节了, 可以 看到这里面 仅仅是分配了一个 给定的 Class 实例的空间
具体的反序列化来创建给定的对象
这部分 主要是从序列化和反序列化的相关实现 开始吧, 以下内容 都仅仅特定描述了 上面单元测试的相关代码, 其他的一些 特殊情况以下内容是没有考虑的
ObjectOutputStream. writeObject
以上 oos.writeObject 到 buffer 里面的字节序列如下
# 序列化的时候字节的概览
$streamMagic, $streamVersion, $TC_OBJECT, $TC_CLASSDESC, $classDesc, $TC_ENDBLOCKDATA, $superDesc[recursely]
$serializeData
$TC_STRING, $len, $xx
$TC_STRING, $len, $12
# 完整的字节序列 : -84 -19 0 5 115 114 0 52 99 111 109 46 104 120 46 116 101 115 116 48 56 82 101 100 110 97 120 101 108 97 70 88 46 84 101 115 116 48 55 85 110 115 97 102 101 65 108 108 111 99 97 116 101 36 80 101 114 115 111 110 93 109 79 61 -74 82 -101 30 2 0 2 76 0 3 97 103 101 116 0 18 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 59 76 0 4 110 97 109 101 113 0 126 0 1 120 112 116 0 2 49 50 116 0 2 120 120
# 拆解的各个字节的意义
$streamMagic
-84 -19
$streamVersion
0 5
$TC_OBJECT
115
$TC_CLASSDESC
114
$nameLen
0 52
$className : com.hx.test08RednaxelaFX.Test07UnsafeAllocate$Person
99 111 109 46 104 120 46 116 101 115 116 48 56 82 101 100 110 97 120 101 108 97 70 88 46 84 101 115 116 48 55 85 110 115 97 102 101 65 108 108 111 99 97 116 101 36 80 101 114 115 111 110
$serialVersionUid : 6732124144459225886
93 109 79 61 -74 82 -101 30
$flags : $SC_SERIALIZABLE
2
$fieldsLen
0 2
$typeOf 'Ljava/lang/String;'
76
$ageLen
0 3
$fieldAge : age
97 103 101
$TC_STRING, $len, Ljava/lang/String;
116 0 18 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 59
$typeOf 'Ljava/lang/String;'
76
$nameLen
0 4
$fieldAge : age
110 97 109 101
$TC_REFERENCE $handleOf'Ljava/lang/String;'
113 0 126 0 1
$TC_ENDBLOCKDATA
120
$superDesc[recursely]
112
$TC_STRING, $len, $xx
116 0 2 49 50
$TC_STRING, $len, $12
116 0 2 120 120
ObjectInputStream. readObject
ois.readObject 这边基本上就是, 读取给定的类的元数据的信息, 然后创建对象, 最后 apply序列化保存的数据
1. 实例化对象的方式
desc. newInstance 创建对象
ObjectStreamClass. getSerializableConstructor : 获取序列化构造器, 寻找的是 给定的 clazz 的第一个没有实现 Serializeable 的 无参构造方法
ReflectionFactory. newConstructorForSerialization : 运行时创建构造方法
生成出来的构造方法逻辑如下
可以 看出这里ConstructorAccessor 和 R大 的字节码, 伪代码 是差不多的, 只是我这里多了一个 Test07UnsafeAllocate.$Creature 的层级
2. apply 对象的属性的方式
使用的是 unsafe 的相关 api 来设置的给定的对象的属性
附
拿取运行时 .class 的方式 : 通过 HSDB 直接创建给定的 class, 默认是放到 当前用户目录, 就比如 : "C:\Users\Jerry.X.He"
或者 是 "jdk1.x/bin" 下面, add at 2019.07.14
反编译给定的字节码文件 : javap -c foo.class
完