ReadObject源码分析第一部分
一句话简述流程:
1 通过读取流获得描述对象
2 通过读取class字节码获取描述对象(获取非静态和transient的字段,获取空构造函数并设置可达)
3 过程中获取/计算并判断serialVersionUID时,执行了静态代码块
4 通过反射调取无参构造函数获取实例,然后反射调用它的readobject()方法
传统入口
try (FileInputStream fileIn = new FileInputStream("person.ser");ObjectInput
Stream in = new ObjectInputStream(fileIn)) {
Person deserializedPerson = (Person) in.readObject();
}
F7进入 readObject(Object.class)
F7进入readObject0(type, false)
case匹配Object进入 checkResolve(readOrdinaryObject(unshared));
先F7跟进readOrdinaryObject(unshared),稍后再看checkResolve方法。
F7进入readClassDesc
readClassDesc 方法主要是创建了一个序列化类描述对象,并且从流中获取类的相关信息
F7进入 readNonProxyDesc(unshared)看看都获取了哪些信息
F7进入readClassDescriptor()
这个方法主要是从流的方向获取反序列化类的一些信息。
F7进入desc.readNonProxy方法
从流中读取反序列化类的一些基础信息,比如类名,serialVersionUID,字段名等保存到desc对象中
回到 readNonProxyDesc()继续往下跟,F7进resolveClass(readDesc)
回到 readNonProxyDesc(),F7进入desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false))方法
这个方法主要是从class字节码的方法获取反序列化类的一些信息,并且和前面从流方向获取到的信息做一个汇总。
F7进入lookup(cl, true)
此方法主要是创建一些数据结构,然后从缓存中查找,如果没有就将序列化的类包装到ObjectStreamClass中,并且将这个ObjectStreamClass对象放到缓存中
F7进入 new ObjectStreamClass(cls)
这里主要关注三点,1获取serialVersionUID时调用AccessController.doPrivileged,2获取非静态和非transient 字段,3获取构造函数的方法getSerializableConstructor()
F7进入getDeclaredSUID(cl)
此处通过反射调获取serialVersionUID,会执行动态代码块,如果没有设置serialVersionUID字段,直接返回null
F7进入getSerialFields
然后再F7进入getSerialFields
F7进入getSerializableConstructor()
可以看出获取的时无参构造方法,而且反射设置了setAccessible(true),所以不管是不是public都可以。
返回initNonProxy
当没有显示的手动创建serialVersionUID时,会进入computeDefaultSUID()计算生成。
F7进入computeDefaultSUID()
此方法会根据反序列化对象的一些属性计算hash生成,这里由于会计算静态代码块的相关东西,所以会执行反序列化类的静态代码块
获取到id后,返回继续执行,后边代码是将model和desc中的一些信息赋值给自己的全局变量,model是传入的,是根据流获取的反序列化类的一些属性,desc是我本方法根据class模板获取的对象信息,执行完方法后,返回readNonProxyDesc方法,最终执行完毕,返回desc这个类描述对象
获取到完整的描述对象后,一路return ,返回到 readOrdinaryObject()方法 F7进入desc.newInstance()
执行我们之前设置的无参构造函数的invoke()
F7 进入 readSerialData(obj, desc);
这里只是去执行自订的readobject()方法,执行完后才继续执行