Java序列化原理初探

writeObject

图片

默认在不反射调用ObjectOutputStream创建实例的时候,调用writeObject0来处理

writeObject0内往下走会调用ObjectStreamClass.lookup方法进行处理

图片

static ObjectStreamClass lookup(Class<?> cl, boolean all) {
        if (!(all || Serializable.class.isAssignableFrom(cl))) { //判断对象实现了序列化接口没有
            return null;
        }
        processQueue(Caches.localDescsQueue, Caches.localDescs);
        WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue);
        Reference<?> ref = Caches.localDescs.get(key);
        Object entry = null;
        if (ref != null) {
            entry = ref.get();
        }
        EntryFuture future = null;
        if (entry == null) {
            EntryFuture newEntry = new EntryFuture();
            Reference<?> newRef = new SoftReference<>(newEntry);
            do {
                if (ref != null) {
                    Caches.localDescs.remove(key, ref);
                }
                ref = Caches.localDescs.putIfAbsent(key, newRef);
                if (ref != null) {
                    entry = ref.get();
                }
            } while (ref != null && entry == null);
            if (entry == null) {
                future = newEntry;
            }
        }

        if (entry instanceof ObjectStreamClass) {  // check common case first
            return (ObjectStreamClass) entry;
        }
        if (entry instanceof EntryFuture) {
            future = (EntryFuture) entry;
            if (future.getOwner() == Thread.currentThread()) {
                /*
                 * Handle nested call situation described by 4803747: waiting
                 * for future value to be set by a lookup() call further up the
                 * stack will result in deadlock, so calculate and set the
                 * future value here instead.
                 */
                entry = null;
            } else {
                entry = future.get();
            }
        }
        if (entry == null) {
            try {
                entry = new ObjectStreamClass(cl); //最终会走到这里 跟入
            } catch (Throwable th) {
                entry = th;
            }
            if (future.set(entry)) {
                Caches.localDescs.put(key, new SoftReference<Object>(entry));
            } else {
                // nested lookup call already set future
                entry = future.get();
            }
        }

        if (entry instanceof ObjectStreamClass) {
            return (ObjectStreamClass) entry;
        } else if (entry instanceof RuntimeException) {
            throw (RuntimeException) entry;
        } else if (entry instanceof Error) {
            throw (Error) entry;
        } else {
            throw new InternalError("unexpected entry: " + entry);
        }
    }

首先会判断接口是否实现了序列化接口,然后会把WeakClassKey对象传入到localDescs去获取(map),最终会走到391行

图片
图片

先是获取类名 然后进行一些相关判断,是否是代理类 是否实现了序列化接口等

往下走会对序列化类的字段进行一些处理

图片

这里是获取类中序列化类的私有字段serialVersionUID

在这里通过反射获取了类中的属性

图片
图片
图片

往下走获取目标类中重写的方法,如readObject writeObject

在初始化完成后,回到writeObject0继续处理

图片
图片

先写入TC_OBJECT(115),然后往下就调用writeClassDesc

图片

最终走到writeNonProxyDesc

图片

writeClassDescriptor里面调用了writeNonProxy

  void writeNonProxy(ObjectOutputStream out) throws IOException {
        out.writeUTF(name);
        out.writeLong(getSerialVersionUID());

        byte flags = 0;
        if (externalizable) {
            flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
            int protocol = out.getProtocolVersion();
            if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
                flags |= ObjectStreamConstants.SC_BLOCK_DATA;
            }
        } else if (serializable) {
            flags |= ObjectStreamConstants.SC_SERIALIZABLE;
        }
        if (hasWriteObjectData) {
            flags |= ObjectStreamConstants.SC_WRITE_METHOD;
        }
        if (isEnum) {
            flags |= ObjectStreamConstants.SC_ENUM;
        }
        out.writeByte(flags);

        out.writeShort(fields.length);
        for (int i = 0; i < fields.length; i++) {
            ObjectStreamField f = fields[i];
            out.writeByte(f.getTypeCode());
            out.writeUTF(f.getName());
            if (!f.isPrimitive()) {
                out.writeTypeString(f.getTypeString());
            }
        }
    }

该方法主要就是写入类的一些信息 如名称 序列化的UID 字段信息

走完流程后接着回到writeOrdinaryObject方法

图片

由于这里类不是代理类也没有实现Externalizable,所以会调writeSerialData

图片

这里判断了用户有没有自己重写writeObject,如果有就调用用户自己写的,由于测试类没有重写,所以最终走到defaultWriteFields方法,在defaultWriteFields就是写入我们类字段的内容

下面是重写writeObject时候的处理

图片
图片

会通过反射调用用户自己创建的writeObject

图片
阅读原文

简介:广东人的安全圈,欢迎关注微信公众号:8ypass
(0)
打赏 喜欢就点个赞支持下吧 喜欢就点个赞支持下吧

声明:本文来自“8ypass”,分享链接:https://www.zyxiao.com/p/297031    侵权投诉

网站客服
网站客服
内容投稿 侵权处理
分享本页
返回顶部