美文网首页
java的序列化与反序列化

java的序列化与反序列化

作者: 特仑苏纯牛乳 | 来源:发表于2019-01-28 16:30 被阅读0次

    1.什么是序列化与反序列化

    序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
    字节的或XML编码格式可以还原完全相等的对象,这个相反的过程又称为反序列化。

    那么为什么需要序列化呢?
    第一种情况是:一般情况下Java对象的声明周期都比Java虚拟机的要短,实际应用中我们希望在JVM停止运行之后能够持久化指定的对象,这时候就需要把对象进行序列化之后保存。
    第二种情况是:需要把Java对象通过网络进行传输的时候。因为数据只能够以二进制的形式在网络中进行传输,因此当把对象通过网络发送出去之前需要先序列化成二进制数据,在接收端读到二进制数据之后反序列化成Java对象。
    我遇到的场景是需要做一个通用缓存组件,将java结果序列化后缓存起来,而后请求数据的时候直接从缓存中获取数据反序列化后返回,这里要处理一个关键问题就是泛型集合数据的类型问题。大家都知道泛型信息是在编译过程中擦除的,怎解决这个关键问题呢,在第二小节中处理。

    2.关键问题(类型信息的处理过程)

    2.1 java原生解决方式

    Java为了方便开发人员将Java对象进行序列化及反序列化提供了一套方便的API来支持。其中包括以下接口和类:
    java.io.Serializable:仅用于标识可序列化的语义
    java.io.Externalizable
    ObjectOutput
    ObjectInput
    ObjectOutputStream
    ObjectInputStream

    @Test
        public void should_test_java_serialize() throws IOException, ClassNotFoundException {
            //given
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
            oos.writeObject(commonResult);
            oos.flush();
            oos.close();
            byte[] bytes = byteArrayOutputStream.toByteArray();
    
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
            CommonResult cachResult = (CommonResult) ois.readObject();
    
            System.out.println(cachResult.getData());
        }
    

    调用ObjectOutputStream.writeObject()和ObjectInputStream.readObject()之后究竟做了什么?byte[]中的数据代表什么意思?
    序列化过程如下:


    image.png

    ObjectOutputStream构造函数:

    public ObjectOutputStream(OutputStream out) throws IOException {
            verifySubclass();
            bout = new BlockDataOutputStream(out);//字节数据容器
            handles = new HandleTable(10, (float) 3.00);
            subs = new ReplaceTable(10, (float) 3.00);
            enableOverride = false;
            writeStreamHeader();//写入头文件
            bout.setBlockDataMode(true);//flush数据
            if (extendedDebugInfo) {
                debugInfoStack = new DebugTraceInfoStack();
            } else {
                debugInfoStack = null;
            }
        }
    
        /**
         * The writeStreamHeader method is provided so subclasses can append or
         * prepend their own header to the stream.  It writes the magic number and
         * version to the stream.
         *
         * @throws  IOException if I/O errors occur while writing to the underlying
         *          stream
         */
        protected void writeStreamHeader() throws IOException {
            bout.writeShort(STREAM_MAGIC);
            bout.writeShort(STREAM_VERSION);
        }
    

    接下来会调用writeObject()方法进行序列化:

    public final void writeObject(Object obj) throws IOException {
            if (enableOverride) {
                writeObjectOverride(obj);
                return;
            }
            try {
                writeObject0(obj, false);
            } catch (IOException ex) {
                if (depth == 0) {
                    writeFatalException(ex);
                }
                throw ex;
            }
        }
    

    writeObject的核心:

        /**
         * Underlying writeObject/writeUnshared implementation.
         */
        private void writeObject0(Object obj, boolean unshared)
            throws IOException
        {
            boolean oldMode = bout.setBlockDataMode(false);
            depth++;
            try {
                // handle previously written and non-replaceable objects
                int h;
                if ((obj = subs.lookup(obj)) == null) {
                    writeNull();
                    return;
                } else if (!unshared && (h = handles.lookup(obj)) != -1) {
                    writeHandle(h);
                    return;
                } else if (obj instanceof Class) {
                    writeClass((Class) obj, unshared);
                    return;
                } else if (obj instanceof ObjectStreamClass) {
                    writeClassDesc((ObjectStreamClass) obj, unshared);
                    return;
                }
    
                // check for replacement object
                Object orig = obj;
                Class<?> cl = obj.getClass();//获取类型信息
                /*ObjectStreamClass这个是类的序列化描述符,
                这个类可以描述需要被序列化的类的元数据,
                包括被序列化的类的名字以及序列号。
                可以通过lookup()方法来查找/创建在这个JVM中加载的特定的ObjectStreamClass对象。*/
                ObjectStreamClass desc;
                for (;;) {
                    // REMIND: skip this check for strings/arrays?
                    Class<?> repCl;
                    desc = ObjectStreamClass.lookup(cl, true);// 创建描述cl的ObjectStreamClass对象
                    if (!desc.hasWriteReplaceMethod() ||
                        (obj = desc.invokeWriteReplace(obj)) == null ||
                        (repCl = obj.getClass()) == cl)
                    {
                        break;
                    }
                    cl = repCl;
                }
                if (enableReplace) {
                    Object rep = replaceObject(obj);
                    if (rep != obj && rep != null) {
                        cl = rep.getClass();
                        desc = ObjectStreamClass.lookup(cl, true);
                    }
                    obj = rep;
                }
    
                // if object replaced, run through original checks a second time
                if (obj != orig) {
                    subs.assign(orig, obj);
                    if (obj == null) {
                        writeNull();
                        return;
                    } else if (!unshared && (h = handles.lookup(obj)) != -1) {
                        writeHandle(h);
                        return;
                    } else if (obj instanceof Class) {
                        writeClass((Class) obj, unshared);
                        return;
                    } else if (obj instanceof ObjectStreamClass) {
                        writeClassDesc((ObjectStreamClass) obj, unshared);
                        return;
                    }
                }
    
                // remaining cases // 根据实际的类型进行不同的写入操作
                if (obj instanceof String) {
                    writeString((String) obj, unshared);
                } else if (cl.isArray()) {
                    writeArray(obj, desc, unshared);
                } else if (obj instanceof Enum) {
                    writeEnum((Enum<?>) obj, desc, unshared);
                } else if (obj instanceof Serializable) {// 被序列化对象实现了Serializable接口
                    writeOrdinaryObject(obj, desc, unshared);
                } else {
                    if (extendedDebugInfo) {
                        throw new NotSerializableException(
                            cl.getName() + "\n" + debugInfoStack.toString());
                    } else {
                        throw new NotSerializableException(cl.getName());
                    }
                }
            } finally {
                depth--;
                bout.setBlockDataMode(oldMode);
            }
        }
    

    序列化过程接下来会执行到writeOrdinaryObject():

     /**
         * Writes representation of a "ordinary" (i.e., not a String, Class,
         * ObjectStreamClass, array, or enum constant) serializable object to the
         * stream.
         */
        private void writeOrdinaryObject(Object obj,
                                         ObjectStreamClass desc,
                                         boolean unshared)
            throws IOException
        {
            if (extendedDebugInfo) {
                debugInfoStack.push(
                    (depth == 1 ? "root " : "") + "object (class \"" +
                    obj.getClass().getName() + "\", " + obj.toString() + ")");
            }
            try {
                desc.checkSerialize();
    
                bout.writeByte(TC_OBJECT); // 写入Object标志位
                writeClassDesc(desc, false);
                handles.assign(unshared ? null : obj);
                if (desc.isExternalizable() && !desc.isProxy()) {
                    writeExternalData((Externalizable) obj);
                } else {
                    writeSerialData(obj, desc);   // 写入被序列化的对象的实例数据
                }
            } finally {
                if (extendedDebugInfo) {
                    debugInfoStack.pop();
                }
            }
        }
    
    aced        Stream Magic
    0005        序列化版本号
    73          标志位:TC_OBJECT,表示接下来是个新的Object
    72          标志位:TC_CLASSDESC,表示接下来是对Class的描述
    0020        类名的长度为32
    636f 6d2e 6265 6175 7479 626f 7373 2e73 com.beautyboss.s
    6c6f 6765 6e2e 5465 7374 4f62 6a65 6374 logen.TestObject
    d3c6 7e1c 4f13 2afe 序列号
    02          flag,可序列化
    00 02       TestObject的字段的个数,为2
    49          TypeCode,I,表示int类型
    0009        字段名长度,占9个字节
    7465 7374 5661 6c75 65      字段名:testValue
    4c          TypeCode:L,表示是个Class或者Interface
    000b        字段名长度,占11个字节
    696e 6e65 724f 626a 6563 74 字段名:innerObject
    74          标志位:TC_STRING,表示后面的数据是个字符串
    0023        类名长度,占35个字节
    4c63 6f6d 2f62 6561 7574 7962 6f73 732f  Lcom/beautyboss/
    736c 6f67 656e 2f49 6e6e 6572 4f62 6a65  slogen/InnerObje
    6374 3b                                  ct;
    78          标志位:TC_ENDBLOCKDATA,对象的数据块描述的结束
    接下来开始写入数据,从父类Parent开始
    
    0000 0064 parentValue的值:100
    0000 012c testValue的值:300
    接下来是写入InnerObject的类元信息
    
    73 标志位,TC_OBJECT:表示接下来是个新的Object
    72 标志位,TC_CLASSDESC:表示接下来是对Class的描述
    0021 类名的长度,为33
    636f 6d2e 6265 6175 7479 626f 7373 com.beautyboss
    2e73 6c6f 6765 6e2e 496e 6e65 724f .slogen.InnerO
    626a 6563 74 bject
    4f2c 148a 4024 fb12 序列号
    02 flag,表示可序列化
    0001 字段个数,1个
    49 TypeCode,I,表示int类型
    00 0a 字段名长度,10个字节
    69 6e6e 6572 5661 6c75 65 innerValue
    78 标志位:TC_ENDBLOCKDATA,对象的数据块描述的结束
    70 标志位:TC_NULL,Null object reference.
    0000 00c8 innervalue的值:200
    

    反序列化:readObject()
    即依次解析以上二进制数据

    有一个需要注意的问题就是,如果子类实现了Serializable接口,但是父类没有实现Serializable接口,这个时候进行反序列化会发生什么情况?

    答:如果父类有默认构造函数的话,即使没有实现Serializable接口也不会有问题,反序列化的时候会调用默认构造函数进行初始化,否则的话反序列化的时候会抛出.InvalidClassException:异常,异常原因为no valid constructor。

    2.2 fastJson等字符串类型的序列化

    这种序列化反序列化处理需要自己设计类型存储结构,坑比较多,会是一个很费时间的工作
    一下是一个有缺陷的实现方式,仅供参考

    public class PcacheJsonUtils {
        private static final Gson gson;
        static {
            ExclusionStrategy es = new ExclusionStrategy() {
                @Override
                public boolean shouldSkipField(FieldAttributes f) {
                    Expose expose = f.getAnnotation(Expose.class);
                    if(null != expose){
                        return !(expose.serialize() && expose.deserialize());
                    }
                    return false;
                }
    
                @Override
                public boolean shouldSkipClass(Class<?> clazz) {
                    if (clazz.isAssignableFrom(Log.class)) {
                        return true;
                    }
                    // 其他log也不参与序列话与反序列化
                    if (clazz.getName().equals("org.slf4j.Logger")) {
                        return true;
                    }
                    return false;
                }
            };
            GsonBuilder gsonBuilder = new GsonBuilder().setExclusionStrategies(es)// .excludeFieldsWithoutExposeAnnotation()
                .setDateFormat("yyyy-MM-dd HH:mm:ss SSS").registerTypeAdapter(Class.class, new TypeAdapter<Class<?>>() {
    
                    @Override
                    public void write(JsonWriter out, Class<?> value) throws IOException {
                        if (value == null) {
                            out.nullValue();
                            return;
                        }
                        out.value(value.getName());
                    }
    
                    @Override
                    public Class<?> read(JsonReader reader) throws IOException {
                        if (reader.peek() == JsonToken.NULL) {
                            reader.nextNull();
                            return null;
                        }
                        String xy = reader.nextString();
                        try {
                            return ClassUtils.getClass(xy);
                        } catch (ClassNotFoundException e) {
                            return null;
                        }
                    }
                });
            gson = gsonBuilder.create();
        }
    
        /**
         *
         * @param o
         *            the object for which Json representation is to be created
         *            setting for Gson
         * @param havClass 是否包含class信息,true:包含
         * @return Json representation of {@code src}.
         */
        public static String toJson(Object o, boolean havClass) {
            if (null == o) {
                return gson.toJson(o);
            }
            String json = gson.toJson(o);
            try {
                if(havClass){
                    JsonObject jo = new JsonObject();
                    jo.setType(o.getClass().getName());
                    if(o instanceof List<?> && !((List<?>) o).isEmpty()){
                        jo.setTypeArguments(((List<?>) o).get(0).getClass().getName());
                    }else if(o instanceof Map<?, ?> && !((Map<?, ?>) o).isEmpty()){
                        Entry<?, ?> entry = ((Map<?, ?>)o).entrySet().iterator().next();
                        jo.setTypeArguments(entry.getKey().getClass().getName(), entry.getValue().getClass().getName());
                    }
                    jo.setData(json);
                    json = gson.toJson(jo);
                }
            } catch (Exception e) {
                log.error("to_json_error,json:" + json, e);
                JsonObject jo = new JsonObject();
                jo.setData(json);
                json = gson.toJson(jo);
            }
            return json;
        }
        /**
         * 参见toJson(Object o, boolean havClass)方法
         * <p>havClass默认为false
         * @param o
         * @return
         */
        public static String toJson(Object o) {
            return toJson(o, false);
        }
    
        /**
         *
         * @param <T>
         *            the type of the desired object
         * @param value
         *            the string from which the object is to be deserialized
         * @param class1
         *            the class of T
         * @return an object of type T from the string. Returns {@code null} if
         *         {@code json} is {@code null}.
         * @throws JsonSyntaxException
         *             if json is not a valid representation for an object of type
         *             classOfT
         */
        @SuppressWarnings("unchecked")
        public static <T> T fromJson(String value, Class<T> class1) {
    //      if (null == value) {
    //          return null;
    //      }
    //      return gson.fromJson(value, class1);
            return (T)fromJson(value, (Type)class1);
        }
    
        /**
         * <pre>
         * Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
         * </pre>
         * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}.
         * @throws JsonParseException if json is not a valid representation for an object of type typeOfT
         * @throws JsonSyntaxException if json is not a valid representation for an object of type
         */
        @SuppressWarnings("unchecked")
        public static <T> T fromJson(String value, Type type) {
            if (null == value) {
                return null;
            }
            return (T)gson.fromJson(value, type);
        }
    
        /**
         * 标准带class的json反编译。自动类型识别
         * @param value
         * @return
         */
        @SuppressWarnings("rawtypes")
        public static Object fromJson(String value){
            JsonObject jo = gson.fromJson(value, JsonObject.class);
            try {
                if(null == jo){
                    return null;
                }
                if(jo.isEmptyData()){
                    Class<?> c = ClassUtils.getClass(jo.ownerType);
                    if(List.class.isAssignableFrom(c)){
                        return new ArrayList();
                    }else if(Map.class.isAssignableFrom(c)){
                        return new HashMap();
                    }
                }
                Type type;
                if(ArrayUtils.isNotEmpty(jo.getTypeArguments())){
                    Class<?>[] cs = new Class<?>[jo.getTypeArguments().length];
                    for(int i=0; i<jo.getTypeArguments().length; i++){
                        cs[i] = ClassUtils.getClass(jo.getTypeArguments()[i]);
                    }
                    type = $Gson$Types.newParameterizedTypeWithOwner(ClassUtils.getClass(jo.ownerType)
                            , ClassUtils.getClass(jo.rawType), cs);
                }else{
                    type = ClassUtils.getClass(jo.ownerType);// $Gson$Types.newParameterizedTypeWithOwner(ClassUtils.forName(jo.ownerType)
                    //, ClassUtils.forName(jo.rawType));
                }
                return gson.fromJson(jo.getData(), type);
            } catch (Exception e) {
                log.error("fromJson, value: " + value + "  jo: "+ jo.getOwnerType() + " ; " + jo.getRawType() + " ; " + jo.getData(), e);
            } catch (LinkageError e) {
                log.error("fromJson, value: " + value + "  jo: "+ jo.getOwnerType() + " ; " + jo.getRawType() + " ; " + jo.getData(), e);
            }
            return null;
        }
    
        /**
         * 序列化数据结构
         */
        static class JsonObject{
            private String ownerType, rawType;
            private String[] typeArguments;
            private String data;
            public void setType(String type) {
                setOwnerType(type);
                setRawType(type);
            }
            public boolean isEmptyData() {
                return StringUtils.isBlank(data) || "[]".equals(data);
            }
            public String getOwnerType() {
                return ownerType;
            }
            public void setOwnerType(String ownerType) {
                this.ownerType = ownerType;
            }
            public String getRawType() {
                return rawType;
            }
            public void setRawType(String rawType) {
                this.rawType = rawType;
            }
            public String[] getTypeArguments() {
                return typeArguments;
            }
            public void setTypeArguments(String... typeArguments) {
                this.typeArguments = typeArguments;
            }
            public String getData() {
                return data;
            }
            public void setData(String data) {
                this.data = data;
            }
        }
    }
    
    

    2.3 Kryo序列化

    kryo是java原生序列化性能十几倍,Kryo序列化机制比默认的Java序列化机制速度要快,序列化后的数据要更小,大概是Java序列化机制的1/10。
    这句话引用oschina对Kryo的解释:Kryo 是一个快速高效的Java对象图形序列化框架,主要特点是性能、高效和易用。该项目用来序列化对象到文件、数据库或者网 络。

    但是,它也有一个致命的弱点:生成的byte数据中部包含field数据,对类升级的兼容性很差!所以,若用kryo序列化对象用于C/S架构的话,两边的Class结构要保持一致。

    kryo
    使用kryo默认的序列化方式fieldSerializer,
    对需要序列化的对象采取默认的操作。开启reference,关闭register

    Kryo在类注册且reference关闭的情况下,序列化速度和大小明显优于hessian和java,接近于protostuff。开启reference后将序列化速度将明显变慢,但仍旧优于hessian。

    2.4 protoStuff

    protocol buffers 是google内部得一种传输协议,目前项目已经开源。它定义了一种紧凑得可扩展得二进制协议格式,适合网络传输,并且针对多个语言有不同得版本可供选择。
    protoBuf优点:1.性能好,效率高;2.代码生成机制,数据解析类自动生成;3.支持向前兼容和向后兼容;4.支持多种编程语言;5.字节数很小,适合网络传输节省io。缺点:1.应用不够广;2.二进制格式导致可读性差;3.缺乏自描述;

    先这样吧,
    心里着急着去……
    参考资料:
    http://www.importnew.com/17964.html
    http://www.importnew.com/20125.html
    http://www.importnew.com/24490.html

    相关文章

      网友评论

          本文标题:java的序列化与反序列化

          本文链接:https://www.haomeiwen.com/subject/kkrujqtx.html