Java 序列化和反序列化(三)Serializable 源碼分析 - 2


Java 序列化和反序列化(三)Serializable 源碼分析 - 2

在上一篇文章中圍繞 ObjectOutputStream#writeObject 講解了一下序列化的整個流程,這中間很多地方涉及到了 ObjectStreamClass 和 ObjectStreamField 這兩個類。

  • ObjectStreamField 按官方的說法是是字段的序列化描述符,本質是對 Field 字段的包裝,包括字段名、字段值等。可以通過 ObjectStreamClass#getFields 獲取所有需要序列化的字段信息。

  • ObjectStreamClass 按官方的說法是類的序列化描述符,本質是對 Class 類的包裝,提取了序列化時類的一些信息,包括字段的描述信息和 serialVersionUID。可以使用 lookup 方法找到/創建在此 Java VM 中加載的具體類的 ObjectStreamClass。

1. ObjectStreamField

ObjectStreamField 只是一個簡單的 JavaBean,保存了序列化過程中字段的元數據信息,包括字段的類型、類型代碼、簽名等。 可以通過 ObjectStreamClass#getFields 獲取所有需要序列化的字段信息。

1.1 數據結構


private final String name;      // 1. field name
private final String signature; // 2. canonical JVM signature of field type
private final Class<?> type;    // 3. 字段類型
private final boolean unshared; // 4. 序列化時字段是否是 unshared
private final Field field;      // 5. Field
private int offset = 0;         // 6. 序列化時數據在 buffer 中的偏移量
  • offset 在序列化的過程中,當一個對象的成員屬性個數超過一個時,JVM 會將會把所有的成員屬性打包成一個“組”來操作,而 offset 就是這個組中當前描述的成員屬性的偏移量,上層的 ObjectStreamClass 在調用當前這個成員屬性的時候就使用偏移量進行引用定位操作;

  • signature 該屬性描述了 JVM 中成員屬性的類型簽名

JavaType TypeCode
byte B
short S
int I
long J
float F
double D
char C
boolean Z
class L
arrary [

1.2 構造函數

public ObjectStreamField(String name, Class<?> type, boolean unshared) {
    this.name = name;
    this.type = type;
    this.unshared = unshared;
    signature = getClassSignature(type).intern();
    field = null;
}

unshared 在 ObjectOutputStream源碼分析的writeObject和writeUnshared區別進行了簡單的說明,這里重點說一下 signature 這個屬性,具體的方法如下:

// JVM 中類型簽名
private static String getClassSignature(Class<?> cl) {
    StringBuilder sbuf = new StringBuilder();
    while (cl.isArray()) {
        sbuf.append('[');
        cl = cl.getComponentType();
    }
    if (cl.isPrimitive()) {
        if (cl == Integer.TYPE) {
            sbuf.append('I');
        } else if (cl == Byte.TYPE) {
            sbuf.append('B');
        } else if (cl == Long.TYPE) {
            sbuf.append('J');
        } else if (cl == Float.TYPE) {
            sbuf.append('F');
        } else if (cl == Double.TYPE) {
            sbuf.append('D');
        } else if (cl == Short.TYPE) {
            sbuf.append('S');
        } else if (cl == Character.TYPE) {
            sbuf.append('C');
        } else if (cl == Boolean.TYPE) {
            sbuf.append('Z');
        } else if (cl == Void.TYPE) {
            sbuf.append('V');
        } else {
            throw new InternalError();
        }
    } else {
        sbuf.append('L' + cl.getName().replace('.', '/') + ';');
    }
    return sbuf.toString();
}

2. ObjectStreamClass

ObjectStreamClass 按官方的說法是類的序列化描述符,本質是對 Class 類的包裝,提取了類序列化時的一些信息,包括字段的描述信息和 serialVersionUID 和需要序列化的字段 fields。本文只介紹一些 ObjectStreamClass 常用用法,更多關於ObjectStreamClass源碼分析

2.1 數據結構

// 類的基本信息
private Class<?> cl;            // 1. Class
private String name;            // 2. cl.getName()
private volatile Long suid;     // 3. serialVersionUID

private boolean isProxy;        // 4. Proxy.isProxyClass(cl)
private boolean isEnum;         // 5. Enum.class.isAssignableFrom(cl)
private boolean serializable;   // 6. Serializable.class.isAssignableFrom(cl)
private boolean externalizable; // 7. Externalizable.class.isAssignableFrom(cl)

// Serializable 接口默認的方法,通過反射調用
private Constructor<?> cons;            // 默認的構造函數
private Method writeObjectMethod;       // writeObject
private Method readObjectMethod;        // readObject
private Method readObjectNoDataMethod;  // readObjectNoData
private Method writeReplaceMethod;      // writeReplace
private Method readResolveMethod;       // readResolve
private boolean hasWriteObjectData;     // writeObjectMethod!=null

// localDesc表示本類的描述信息,superDesc表示父類的描述信息
private ObjectStreamClass localDesc;    // this
private ObjectStreamClass superDesc;    // 父類,superDesc=lookup(superCl, false)

// 要序列化的字段信息,通過 getSerialFields(c1) 獲取
private ObjectStreamField[] fields;     // 序列化的字段信息

總結: 通過這些字段信息可以看到 ObjectStreamClass 提取了類序列化時的一些基本信息,這些信息大部分都是在其構造時就提取出來了。

2.2 構造函數

private ObjectStreamClass(final Class<?> cl) {
    // 1. 類的基本信息獲取
    this.cl = cl;
    name = cl.getName();
    isProxy = Proxy.isProxyClass(cl);
    isEnum = Enum.class.isAssignableFrom(cl);
    serializable = Serializable.class.isAssignableFrom(cl);
    externalizable = Externalizable.class.isAssignableFrom(cl);

    Class<?> superCl = cl.getSuperclass();
    superDesc = (superCl != null) ? lookup(superCl, false) : null;
    localDesc = this;

    // 2. Serializable 接口默認的方法,通過反射調用
    if (serializable) {       
        if (isEnum) {
            suid = Long.valueOf(0);
            fields = NO_FIELDS;
            return null;
        }
        if (cl.isArray()) {
            fields = NO_FIELDS;
            return null;
        }

        suid = getDeclaredSUID(cl);         // serialVersionUID
        try {
            fields = getSerialFields(cl);   // 序列化的字段信息
            computeFieldOffsets();
        } catch (InvalidClassException e) {
            serializeEx = deserializeEx = new ExceptionInfo(e.classname, e.getMessage());
            fields = NO_FIELDS;
        }

        if (externalizable) {
            cons = getExternalizableConstructor(cl);
        } else {
            cons = getSerializableConstructor(cl);
            writeObjectMethod = getPrivateMethod(cl, "writeObject",
                new Class<?>[] { ObjectOutputStream.class }, Void.TYPE);
            readObjectMethod = getPrivateMethod(cl, "readObject",
                new Class<?>[] { ObjectInputStream.class }, Void.TYPE);
            readObjectNoDataMethod = getPrivateMethod(cl, "readObjectNoData", null, Void.TYPE);
            hasWriteObjectData = (writeObjectMethod != null);
        }
        writeReplaceMethod = getInheritableMethod(cl, "writeReplace", null, Object.class);
        readResolveMethod = getInheritableMethod(cl, "readResolve", null, Object.class);
        return null;
    } else {
        suid = Long.valueOf(0);
        fields = NO_FIELDS;
    }

    // 省略異常處理 ...
    initialized = true;
}

總結: 這個構造函數是私有的,可以通過 lookup 獲取一個類的 ObjectStreamClass。

// false 表示只獲取實現了 Serializable 接口的類
public static ObjectStreamClass lookup(Class<?> cl) {
    return lookup(cl, false);
}
// true 表示不管是否實現這個接口都提取相關的信息
public static ObjectStreamClass lookupAny(Class<?> cl) {
    return lookup(cl, true);
}

2.3 提取序列號:getDeclaredSUID

// 提取 serialVersionUID 字段信息
private static Long getDeclaredSUID(Class<?> cl) {
    try {
        Field f = cl.getDeclaredField("serialVersionUID");
        int mask = Modifier.STATIC | Modifier.FINAL;
        if ((f.getModifiers() & mask) == mask) {
            f.setAccessible(true);
            return Long.valueOf(f.getLong(null));
        }
    } catch (Exception ex) {
    }
    return null;
}

public long getSerialVersionUID() {
    if (suid == null) {                 // 顯示的配置了 serialVersionUID 就直接返回
        return computeDefaultSUID(cl);  // 生成一個默認的序列號 id
    }
    return suid.longValue();
}

總結: getDeclaredSUID 方法提取 serialVersionUID 字段信息。如果沒有配置,getSerialVersionUID 方法會通過 computeDefaultSUID 生成一個默認的序列號。

2.4 提取需要序列化字段:getSerialFields

// 提取需要序列化字段
private static ObjectStreamField[] getSerialFields(Class<?> cl)
    throws InvalidClassException {
    ObjectStreamField[] fields;
    if (Serializable.class.isAssignableFrom(cl) &&
        !Externalizable.class.isAssignableFrom(cl) &&
        !Proxy.isProxyClass(cl) &&
        !cl.isInterface()) {
        // serialPersistentFields 配置需要序列化的字段
        if ((fields = getDeclaredSerialFields(cl)) == null) {
            // 默認的序列化字段
            fields = getDefaultSerialFields(cl);
        }
        Arrays.sort(fields);
    } else {
        fields = NO_FIELDS;
    }
    return fields;
}

// 對外暴露的方法,獲取可序列化的字段
public ObjectStreamField[] getFields() {
    return getFields(true);
}
    ObjectStreamField[] getFields(boolean copy) {
    return copy ? fields.clone() : fields;
}

總結: getDeclaredSerialFields 提取的是 serialPersistentFields 字段顯示配置的 serialPersistentFields 需要序列化的字段,如果沒有配置(大部分情況都是這樣的)則提取默認的字段。

// getDeclaredFields 提取本類中的所有字段,只要不是 static 或 transient 修辭的都會序列化
private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
    Field[] clFields = cl.getDeclaredFields();
    ArrayList<ObjectStreamField> list = new ArrayList<>();
    int mask = Modifier.STATIC | Modifier.TRANSIENT;

    for (int i = 0; i < clFields.length; i++) {
        if ((clFields[i].getModifiers() & mask) == 0) {
            list.add(new ObjectStreamField(clFields[i], false, true));
        }
    }
    int size = list.size();
    return (size == 0) ? NO_FIELDS :
        list.toArray(new ObjectStreamField[size]);
}

2.5 其它方法

private void writeSerialData(Object obj, ObjectStreamClass desc) throws IOException {
    // 獲取要序列化的類,包括實現了 Serializable 接口的父類
    ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
    for (int i = 0; i < slots.length; i++) {  
        defaultWriteFields(obj, slots[i].desc);
    }
}

private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException {
    // 1. Java 原生類型序列化
    int primDataSize = desc.getPrimDataSize();      // 1.1 獲取原生類型字段的長度
    if (primVals == null || primVals.length < primDataSize) {
        primVals = new byte[primDataSize];
    }
    desc.getPrimFieldValues(obj, primVals);         // 1.2 獲取原生類型字段的值
    bout.write(primVals, 0, primDataSize, false);   // 1.3 原生類型序列化

    // 2. Java 對象類型序列化,遞歸調用 writeObject0 方法
    ObjectStreamField[] fields = desc.getFields(false);     // 2.1 獲取所有序列化的字段
    Object[] objVals = new Object[desc.getNumObjFields()];
    int numPrimFields = fields.length - objVals.length;
    desc.getObjFieldValues(obj, objVals);                   // 2.2 獲取所有序列化字段的值
    for (int i = 0; i < objVals.length; i++) {              // 2.3 遞歸完成序列化
        writeObject0(objVals[i], fields[numPrimFields + i].isUnshared());            
    }
}

總結: 其它用到的方法了解一下用法即可,就不往下深究了。

參考:

  1. 《ObjectStreamClass源碼分析》:https://blog.csdn.net/silentbalanceyh/article/details/8250096

每天用心記錄一點點。內容也許不重要,但習慣很重要!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM