博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
fastjson深度源码解析- 序列化(一) - 序列化基础类型解析
阅读量:4053 次
发布时间:2019-05-25

本文共 8506 字,大约阅读时间需要 28 分钟。

首先感谢高铁同学同意我撰写源码解析并且给予了必要的指导,fastjson是一个高效的json与java对象序列化框架,很多公司和开源框架都从fastjson中受益。

目前网上公开的fastjson源码解析太少或者缺少深度,因此我计划通过研究源码的方式并记录下来,让更多想了解底层实现的同学受益。如果在阅读过程中发现错误,欢迎与我沟通 。

邮箱:jason.shang@hotmail.com微信:skyingshang

可以在线阅读gitbook,也可以参考已经添加注释的源码。

我在工作之余编写源码解析的目的:

  1. 做技术应该追求极致和细节,让更多的人拥抱开源并从中受益
  2. 深入理解fastjson作者的设计思想
  3. 掌握基本的词法和语法分析实现
  4. 源码是最好的教材,降低阅读开源代码的成本
  5. 巩固技术基础
  6. 分享是一种美德

为了尊重作者的劳动,如果您转载请保留以下内容:

文章作者 : 诣极(商宗海)框架作者 : 高铁文章地址 : https://zonghaishang.gitbooks.io/fastjson-source-code-analysis/content/代码地址 : https://github.com/zonghaishang/fastjson框架地址 : https://github.com/alibaba/fastjson

概要

fastjson核心功能包括序列化和反序列化,序列化的含义是将java对象转换成跨语言的json字符串。我认为从这里作为分析入口相对比较简单,第二章会从反序列化角度切入,会包含词法分析等较为复杂点展开。

现在,我们正式开始咀嚼原汁原味的代码吧,我添加了详细的代码注释。

SerializeWriter成员变量

com.alibaba.fastjson.serializer.SerializeWriter类非常重要,序列化输出都是通过转换底层操作,重要字段如下:

/** 字符类型buffer */    private final static ThreadLocal
bufLocal = new ThreadLocal
(); /** 字节类型buffer */ private final static ThreadLocal
bytesBufLocal = new ThreadLocal
(); /** 存储序列化结果buffer */ protected char buf[]; /** buffer中包含的字符数 */ protected int count; /** 序列化的特性,比如写枚举按照名字还是枚举值 */ protected int features; /** 序列化输出器 */ private final Writer writer; /** 是否使用单引号输出json */ protected boolean useSingleQuotes; /** 输出字段是否追加 "和:字符 */ protected boolean quoteFieldNames; /** 是否对字段排序 */ protected boolean sortField; /** 禁用字段循环引用探测 */ protected boolean disableCircularReferenceDetect; protected boolean beanToArray; /** 按照toString方式获取对象字面值 */ protected boolean writeNonStringValueAsString; /** 如果字段默认值不输出,比如原型int,默认值0不输出 */ protected boolean notWriteDefaultValue; /** 序列化枚举时使用枚举name */ protected boolean writeEnumUsingName; /** 序列化枚举时使用枚举toString值 */ protected boolean writeEnumUsingToString; protected boolean writeDirect; /** key分隔符,默认单引号是',双引号是" */ protected char keySeperator; protected int maxBufSize = -1; protected boolean browserSecure; protected long sepcialBits;

SerializeWriter成员函数

序列化整形数字

public void writeInt(int i) {        /** 如果是整数最小值,调用字符串函数输出到缓冲区*/        if (i == Integer.MIN_VALUE) {            write("-2147483648");            return;        }        /** 根据数字判断占用的位数,负数会多一位用于存储字符`-` */        int size = (i < 0) ? IOUtils.stringSize(-i) + 1 : IOUtils.stringSize(i);        int newcount = count + size;        /** 如果当前存储空间不够 */        if (newcount > buf.length) {            if (writer == null) {                /** 扩容到为原有buf容量1.5倍+1, copy原有buf的字符*/                expandCapacity(newcount);            } else {                char[] chars = new char[size];                /** 将整数i转换成单字符并存储到chars数组 */                IOUtils.getChars(i, size, chars);                /** 将chars字符数组内容写到buffer中*/                write(chars, 0, chars.length);                return;            }        }        /** 如果buffer空间够,直接将字符写到buffer中 */        IOUtils.getChars(i, newcount, buf);        /** 重新计数buffer中字符数 */        count = newcount;    }

其中值得提一下的是IOUtils.getChars,里面利用了Integer.getChars(int i, int index, char[] buf),主要的思想是整数超过65536 进行除以100, 循环取出数字后两位,依次将个位和十位转换为单字符,如果整数小于等于65536,进行除以10,取出个位数字并转换单字符,getCharts中 q = (i * 52429) >>> (16+3),可以理解为 (i乘以0.1), 但是精度更高。

序列化长整形数字

public void writeLong(long i) {        boolean needQuotationMark = isEnabled(SerializerFeature.BrowserCompatible) //                                    && (!isEnabled(SerializerFeature.WriteClassName)) //                                    && (i > 9007199254740991L || i < -9007199254740991L);        if (i == Long.MIN_VALUE) {            if (needQuotationMark) write("\"-9223372036854775808\"");            /** 如果是长整数最小值,调用字符串函数输出到缓冲区*/            else write("-9223372036854775808");            return;        }        /** 根据数字判断占用的位数,负数会多一位用于存储字符`-` */        int size = (i < 0) ? IOUtils.stringSize(-i) + 1 : IOUtils.stringSize(i);        int newcount = count + size;        if (needQuotationMark) newcount += 2;        /** 如果当前存储空间不够 */        if (newcount > buf.length) {            if (writer == null) {                /** 扩容到为原有buf容量1.5倍+1, copy原有buf的字符*/                expandCapacity(newcount);            } else {                char[] chars = new char[size];                /** 将长整数i转换成单字符并存储到chars数组 */                IOUtils.getChars(i, size, chars);                if (needQuotationMark) {                    write('"');                    write(chars, 0, chars.length);                    write('"');                } else {                    write(chars, 0, chars.length);                }                return;            }        }        /** 添加引号 */        if (needQuotationMark) {            buf[count] = '"';            IOUtils.getChars(i, newcount - 1, buf);            buf[newcount - 1] = '"';        } else {            IOUtils.getChars(i, newcount, buf);        }        count = newcount;    }

序列化长整型和整型非常类似,增加了双引号判断,采用用了和Integer转换为单字符同样的技巧。

序列化浮点类型数字

public void writeDouble(double doubleValue, boolean checkWriteClassName) {        /** 如果doubleValue不合法或者是无穷数,调用writeNull */        if (Double.isNaN(doubleValue)                || Double.isInfinite(doubleValue)) {            writeNull();        } else {            /** 将高精度double转换为字符串 */            String doubleText = Double.toString(doubleValue);            /** 启动WriteNullNumberAsZero特性,会将结尾.0去除 */            if (isEnabled(SerializerFeature.WriteNullNumberAsZero) && doubleText.endsWith(".0")) {                doubleText = doubleText.substring(0, doubleText.length() - 2);            }            /** 调用字符串输出方法 */            write(doubleText);            /** 如果开启序列化WriteClassName特性,输出Double类型 */            if (checkWriteClassName && isEnabled(SerializerFeature.WriteClassName)) {                write('D');            }        }    }     public void writeFloat(float value, boolean checkWriteClassName) {        /** 如果value不合法或者是无穷数,调用writeNull */        if (Float.isNaN(value) //                || Float.isInfinite(value)) {            writeNull();        } else {            /** 将高精度float转换为字符串 */            String floatText= Float.toString(value);            /** 启动WriteNullNumberAsZero特性,会将结尾.0去除 */            if (isEnabled(SerializerFeature.WriteNullNumberAsZero) && floatText.endsWith(".0")) {                floatText = floatText.substring(0, floatText.length() - 2);            }            write(floatText);            /** 如果开启序列化WriteClassName特性,输出float类型 */            if (checkWriteClassName && isEnabled(SerializerFeature.WriteClassName)) {                write('F');            }        }    }

序列化浮点类型的基本思路是先转换为字符串,然后在输出到输出流中。

序列化枚举类型

public void writeEnum(Enum
value) { if (value == null) { /** 如果枚举value为空,调用writeNull输出 */ writeNull(); return; } String strVal = null; /** 如果开启序列化输出枚举名字作为属性值 */ if (writeEnumUsingName && !writeEnumUsingToString) { strVal = value.name(); } else if (writeEnumUsingToString) { /** 采用枚举默认toString方法作为属性值 */ strVal = value.toString();; } if (strVal != null) { /** 如果开启引号特性,输出json包含引号的字符串 */ char quote = isEnabled(SerializerFeature.UseSingleQuotes) ? '\'' : '"'; write(quote); write(strVal); write(quote); } else { /** 输出枚举所在的索引号 */ writeInt(value.ordinal()); } }

序列化单字符

public void write(int c) {        int newcount = count + 1;        /** 如果当前存储空间不够 */        if (newcount > buf.length) {            if (writer == null) {                expandCapacity(newcount);            } else {                /** 强制流输出并刷新缓冲区 */                flush();                newcount = 1;            }        }        /** 存储单字符到buffer并更新计数 */        buf[count] = (char) c;        count = newcount;    }

序列化Null

public void writeNull() {        /** 调用输出字符串null */        write("null");    }

序列化Boolean

public void write(boolean value) {        if (value) {            /** 输出true字符串 */            write("true");        } else {            /** 输出false字符串 */            write("false");        }    }
你可能感兴趣的文章
iOS QQ侧滑菜单(高仿)
查看>>
iOS 扫一扫功能开发
查看>>
iOS app之间的跳转以及传参数
查看>>
iOS __block和__weak的区别
查看>>
Android(三)数据存储之XML解析技术
查看>>
Spring JTA应用之JOTM配置
查看>>
spring JdbcTemplate 的若干问题
查看>>
Servlet和JSP的线程安全问题
查看>>
GBK编码下jQuery Ajax中文乱码终极暴力解决方案
查看>>
Oracle 物化视图
查看>>
PHP那点小事--三元运算符
查看>>
解决国内NPM安装依赖速度慢问题
查看>>
Brackets安装及常用插件安装
查看>>
Centos 7(Linux)环境下安装PHP(编译添加)相应动态扩展模块so(以openssl.so为例)
查看>>
fastcgi_param 详解
查看>>
Nginx配置文件(nginx.conf)配置详解
查看>>
标记一下
查看>>
IP报文格式学习笔记
查看>>
autohotkey快捷键显示隐藏文件和文件扩展名
查看>>
Linux中的进程
查看>>