1、代码
Info.java //创建一个info类,该类引用接口Serializable,该类可以序列化和反序列化
引用了Serializable接口时,会在类内寻找writeObject(readObject)方法,如果重写了该方法则使用重写后的, 如果未重写,则使用默认的
关键函数resolveClass(),通过java反射机制,获取重写后的readObject方法,这里是因为原本的java有readobject方法,必须通过java反射机制在执行时重写readobject
执行流程如下
ObjectInputSteram.readObject()
readObject0()
readOrdinaryObject()
desc = readClassDesc(false)
descriptor = readNonProxyDesc(unshared)
readDesc = readClassDescriptor()
cl = resolveClass(readDesc)
filterCheck(cl, -1)
desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false))
各种初始化、检查 suid 等
return desc
return descriptor
obj = desc.isInstantiable() ? desc.newInstance() : null
readSerialData(obj, desc)
slotDesc.invokeReadObject(obj, this)
readObjectMethod.invoke(obj, new Object[]{ in })
测试代码
接口类Info
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class Info implements Serializable { //引用Serializable类,一个标志位,标志该类可以序列化和反序列化
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private void readObject(ObjectInputStream s) throws IOException{ //重写了Serializable的readobject方法,实现反序列化时的输出,注意这里的类修饰方法必须是private
System.out.println("test");
}
}
主类Main
实现序列化和反序列化函数,并且调用
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
Info i = new Info(); //实例化类Info
i.setName("alpenliebe");
byte[] bytes = serialize(i); //序列化类Info
Info i2 = (Info)unserialize(bytes); //反序列化类Info
System.out.println(i2.getName());
}
public static byte[] serialize(Object obj) throws IOException { //序列化函数,读入一个object返回一个字节流
ByteArrayOutputStream baos = new ByteArrayOutputStream(); //申请一块数组缓冲区
ObjectOutputStream oos = new ObjectOutputStream(baos); //实例化对象到字节的对象,需要一块数组的缓冲区
oos.writeObject(obj); //对象到字节的对象调用写入对象,把对象写入到缓冲区中
return baos.toByteArray(); //把缓冲区的内容序列化成字节流
}
public static Object unserialize(byte[] bytes) throws Exception{ //反序列化函数,读入一个字节流,返回一个object
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); //申请一个输入字节流空间,并将字节流传入到空间内
return ois.readObject(); //把缓冲区的内容反序列化成对象
}
}
2、调试分析
在readObject()处打上断点开始分析,跟进readObject()
首先执行一个enableOverride判断,只要是由带参数的 constructor 建立的 ObjectInputStream 实力,这个参数的值预设就是 false
当constructor 沒有参数時,才会将 enableOverride设成 true:
这里没什么用,接着下来执行readObject0()
跟入readObject0()
BlockDataInputStream是ObjectInputStream底层的资料读取类,用来完成对序列化Stream的读取
其分分为两种读取模式: Default mode 和 Block mode
从代码里可以看到,如果是 Block mode,会检查当前 block 是否有剩余的 bytes,都没有就转 Default mode
接着 tc = bin.peekByte()会去呼叫 PeekInputStream.peek()
这个 PeekInputStream 类背后是继承 InputStream 类,最后呼叫的是 InputStream.read()
所以其实 tc 就是从序列化 Stream 中读一个 Byte 出來
以我门前面的测试 class那个例子來说,可以用 SerializationDumper 这个工具来查看反序列化流具体执行过程
根据其结果,可以知道 tc 会走到 TC_OBJECT
private Object readObject0(Class<?> type, boolean unshared) throws IOException {
boolean oldMode = bin.getBlockDataMode(); //这里由constructor做初始化,序列号stream的读取器
if (oldMode) {
int remain = bin.currentBlockRemaining();
if (remain > 0) {
throw new OptionalDataException(remain);
} else if (defaultDataEnd) {
/*
* Fix for 4360508: stream is currently at the end of a field
* value block written via default serialization; since there
* is no terminating TC_ENDBLOCKDATA tag, simulate
* end-of-custom-data behavior explicitly.
*/
throw new OptionalDataException(true);
}
bin.setBlockDataMode(false);
}
byte tc;
while ((tc = bin.peekByte()) == TC_RESET) {
bin.readByte();
handleReset();
}
depth++;
totalObjectRefs++;
try {
switch (tc) {
case TC_NULL:
return readNull();
case TC_REFERENCE:
// check the type of the existing object
return type.cast(readHandle(unshared));
case TC_CLASS:
if (type == String.class) {
throw new ClassCastException("Cannot cast a class to java.lang.String");
}
return readClass(unshared);
case TC_CLASSDESC:
case TC_PROXYCLASSDESC:
if (type == String.class) {
throw new ClassCastException("Cannot cast a class to java.lang.String");
}
return readClassDesc(unshared);
case TC_STRING:
case TC_LONGSTRING:
return checkResolve(readString(unshared));
case TC_ARRAY:
if (type == String.class) {
throw new ClassCastException("Cannot cast an array to java.lang.String");
}
return checkResolve(readArray(unshared));
case TC_ENUM:
if (type == String.class) {
throw new ClassCastException("Cannot cast an enum to java.lang.String");
}
return checkResolve(readEnum(unshared));
case TC_OBJECT:
if (type == String.class) {
throw new ClassCastException("Cannot cast an object to java.lang.String");
}
return checkResolve(readOrdinaryObject(unshared));
case TC_EXCEPTION:
if (type == String.class) {
throw new ClassCastException("Cannot cast an exception to java.lang.String");
}
IOException ex = readFatalException();
throw new WriteAbortedException("writing aborted", ex);
case TC_BLOCKDATA:
case TC_BLOCKDATALONG:
if (oldMode) {
bin.setBlockDataMode(true);
bin.peek(); // force header read
throw new OptionalDataException(
bin.currentBlockRemaining());
} else {
throw new StreamCorruptedException(
"unexpected block data");
}
case TC_ENDBLOCKDATA:
if (oldMode) {
throw new OptionalDataException(true);
} else {
throw new StreamCorruptedException(
"unexpected end of block data");
}
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
常量TC_OBJECT 对应的整数是 0x73
(参考https://github.com/AdoptOpenJDK/openjdk-jdk8u/blob/aa318070b27849f1fe00d14684b2a40f7b29bf79/jdk/src/share/classes/java/io/ObjectStreamConstants.java#L72)
代表读进来的是个 object
case TC_OBJECT:
if (type == String.class) {
throw new ClassCastException("Cannot cast an object to java.lang.String");
}
return checkResolve(readOrdinaryObject(unshared));
继续跟进 checkResolve(readOrdinaryObject(unshared)) 中的readOrdinaryObject(unshared)
开头执行了个判断,如果不是TC_OBJECT传入的,直接返回一个错误,接下来呼叫readClassDesc
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
Class<?> cl = desc.forClass();
if (cl == String.class || cl == Class.class
|| cl == ObjectStreamClass.class) {
throw new InvalidClassException("invalid class descriptor");
}
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
passHandle = handles.assign(unshared ? unsharedMarker : obj);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(passHandle, resolveEx);
}
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}
handles.finish(passHandle);
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
// Filter the replacement object
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
跟进readClassDesc()
这个函数的功能就跟方法名描述的一样,会尝试从序列化 Stream 中,构造出 class descriptor
以这个栗子来说,第一个 Byte 读到的会是 TC_CLASSDESC (0x72),代表 Class Descriptor,就算一种用来描述类的结构,包含类名字、成员类型等描述
所以接下來会呼叫 descriptor = readNonProxyDesc(unshared) 来读出这个 class descriptor
private ObjectStreamClass readClassDesc(boolean unshared)
throws IOException
{
byte tc = bin.peekByte();
ObjectStreamClass descriptor;
switch (tc) {
case TC_NULL:
descriptor = (ObjectStreamClass) readNull();
break;
case TC_REFERENCE:
descriptor = (ObjectStreamClass) readHandle(unshared);
break;
case TC_PROXYCLASSDESC:
descriptor = readProxyDesc(unshared);
break;
case TC_CLASSDESC:
descriptor = readNonProxyDesc(unshared);
break;
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
if (descriptor != null) {
validateDescriptor(descriptor);
}
return descriptor;
}
跟入readNonProxyDesc(unshared)
这里会先初始化一个ObjectStreamClass变量desc,他代表的就是序列化 class descriptor
接着后面呼叫 readClassDescriptor()方法
private ObjectStreamClass readNonProxyDesc(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_CLASSDESC) {
throw new InternalError();
}
ObjectStreamClass desc = new ObjectStreamClass();
int descHandle = handles.assign(unshared ? unsharedMarker : desc);
passHandle = NULL_HANDLE;
ObjectStreamClass readDesc = null;
try {
readDesc = readClassDescriptor();
} catch (ClassNotFoundException ex) {
throw (IOException) new InvalidClassException(
"failed to read class descriptor").initCause(ex);
}
Class<?> cl = null;
ClassNotFoundException resolveEx = null;
bin.setBlockDataMode(true);
final boolean checksRequired = isCustomSubclass();
try {
if ((cl = resolveClass(readDesc)) == null) {
resolveEx = new ClassNotFoundException("null class");
} else if (checksRequired) {
ReflectUtil.checkPackageAccess(cl);
}
} catch (ClassNotFoundException ex) {
resolveEx = ex;
}
// Call filterCheck on the class before reading anything else
filterCheck(cl, -1);
skipCustomData();
try {
totalObjectRefs++;
depth++;
desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
} finally {
depth--;
}
handles.finish(descHandle);
passHandle = descHandle;
return desc;
}
跟进readClassDescriptor()方法
内部一样会去初始化一个 ObjectStreamClass 变量
接着呼叫readNonProxy()
protected ObjectStreamClass readClassDescriptor()
throws IOException, ClassNotFoundException
{
ObjectStreamClass desc = new ObjectStreamClass();
desc.readNonProxy(this);
return desc;
}
跟进readNonProxy()
其中readUTF读取上面说的 class descriptor表示的名字
suid = Long.valueOf(in.readLong())就是读出serialVersionUID,版本标识位一般没什么用,如果不一致会抛出异常
执行完毕,上层函数readClassDescriptor()会把结果回传
void readNonProxy(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
name = in.readUTF();
suid = Long.valueOf(in.readLong());
isProxy = false;
byte flags = in.readByte();
hasWriteObjectData =
((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
hasBlockExternalData =
((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
externalizable =
((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
boolean sflag =
((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
if (externalizable && sflag) {
throw new InvalidClassException(
name, "serializable and externalizable flags conflict");
}
serializable = externalizable || sflag;
isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
if (isEnum && suid.longValue() != 0L) {
throw new InvalidClassException(name,
"enum descriptor has non-zero serialVersionUID: " + suid);
}
int numFields = in.readShort();
if (isEnum && numFields != 0) {
throw new InvalidClassException(name,
"enum descriptor has non-zero field count: " + numFields);
}
fields = (numFields > 0) ?
new ObjectStreamField[numFields] : NO_FIELDS;
for (int i = 0; i < numFields; i++) {
char tcode = (char) in.readByte();
String fname = in.readUTF();
String signature = ((tcode == 'L') || (tcode == '[')) ?
in.readTypeString() : new String(new char[] { tcode });
try {
fields[i] = new ObjectStreamField(fname, signature, false);
} catch (RuntimeException e) {
throw (IOException) new InvalidClassException(name,
"invalid descriptor for field " + fname).initCause(e);
}
}
computeFieldOffsets();
}
刚刚初始化完的 class descriptor readDesc 执行到 resolveClass()
而 resolveClass() 做的事情很简单,通过反射,取得并回传当前 descriptor 描述的类,也就是我们代码中的Main这个类
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException
{
String name = desc.getName();
try {
return Class.forName(name, false, latestUserDefinedLoader());
} catch (ClassNotFoundException ex) {
Class<?> cl = primClasses.get(name);
if (cl != null) {
return cl;
} else {
throw ex;
}
}
}
接着执行到filterCheck(cl, -1)中,这里的cl就是刚才resolveClass()回传的结果
这里可以看到 serialFilter 是在 ObjectInputStream 初始化时取得的
当serialFilter 存在时,filtercheck 会去做检查、过滤,如果没通过就直接拋出异常
serialFilter = ObjectInputFilter.Config.getSerialFilter();
接着执行到readNonProxyDesc类中的desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false))
private void filterCheck(Class<?> clazz, int arrayLength)
throws InvalidClassException {
if (serialFilter != null) {
RuntimeException ex = null;
ObjectInputFilter.Status status;
// Info about the stream is not available if overridden by subclass, return 0
long bytesRead = (bin == null) ? 0 : bin.getBytesRead();
try {
status = serialFilter.checkInput(new FilterValues(clazz, arrayLength,
totalObjectRefs, depth, bytesRead));
} catch (RuntimeException e) {
// Preventive interception of an exception to log
status = ObjectInputFilter.Status.REJECTED;
ex = e;
}
if (status == null ||
status == ObjectInputFilter.Status.REJECTED) {
// Debug logging of filter checks that fail
if (Logging.infoLogger != null) {
Logging.infoLogger.info(
"ObjectInputFilter {0}: {1}, array length: {2}, nRefs: {3}, depth: {4}, bytes: {5}, ex: {6}",
status, clazz, arrayLength, totalObjectRefs, depth, bytesRead,
Objects.toString(ex, "n/a"));
}
InvalidClassException ice = new InvalidClassException("filter status: " + status);
ice.initCause(ex);
throw ice;
} else {
// Trace logging for those that succeed
if (Logging.traceLogger != null) {
Logging.traceLogger.finer(
"ObjectInputFilter {0}: {1}, array length: {2}, nRefs: {3}, depth: {4}, bytes: {5}, ex: {6}",
status, clazz, arrayLength, totalObjectRefs, depth, bytesRead,
Objects.toString(ex, "n/a"));
}
}
}
}
跟进initNonProxy()
这个方法做了很多初始化操作
包括前面说的 suid 检查、计算等,在这个方法中都会处理
参数 model 是刚刚从序列化 Stream 中,读出来的 readDesc,而目前 initNonProxy 这个方法是由前面建立的 desc 呼叫的
这个方法会使用 readDesc (反序列化还原出來的) 属性来初始化 desc,所以必須先检查 readDesc 正确性
为了检查 readDesc 正确性,会判断跟本地直接 new 出来的localDesc 的 suid, class name 等内容是否相同,如果不同抛出异常
其中 localDesc = lookup(cl, true) 是根据 class,返回对应的 class descriptor: