fastjson那些事
如果无法正常显示,请先停止浏览器的去广告插件。
1. FASTJSON 那些事
阿里中间件 温绍锦
2017.07.26
为了无法计算的价值
2. FASTJSON 发展历程
2011
2017.1
开源 & 发展
安全漏洞
2011.1 开源到 alibabatech.org
2011.7 开源到 github.com/alibaba
github.com/eishay/jvm-serializers 评测 json/databind 分类最快
json-lib 太慢
Jackson-1.x API 不友好
Jackson-1.x LGPG 协议
项目创建
2010.12
2017.1.27 除夕确认漏洞开始修复
2017.3.15 内部修复完毕对外公告
漏洞披露规范未造成严重影响
2012 年最受欢迎中国开源软件
2013 年开源中国 10 大热门开源项目
2016 年最受欢迎中国开源软件
2016 年发布 1.1.52.android 针对 android 做性能优化
2017 年 maven 中央仓库月下载超过 15 万
被广泛采用
2012~2017
为了无法计算的价值
3. 协议类型 序列化 反序列化 json/dsl-platform 文本 526 806 Eishay 性能对
比
json-array/fastjson/databind 文本 650 696 基于数组性能极致
msgpack/databind 二进制 796 1052 protobuf 二进制 1173 719 json/fastjson/databind 文本 1058 1241 thrift 二进制 1455 731 json/jackson/databind 文本 1164 1866 hessian 文本 2842 4622 json/gson/databind 文本 4667 4403 bson/jackson/databind 二进制 4105 5449 java-built-in 二进制 5046 23279 json/json-lib/databind 文本 19853 71969
数据来源 https://github.com/eishay/jvm-serializers/wiki
json/databind 分类最
快
比流行的二进制协议快!
为了无法计算的价值
4. Java JSON 库同类产品对比
fastjson fastjson-android jackson gson moshi
最新版本 1.2.35 1.1.60.android 2.8.9 2.8.1 1.5.0
最新版大小 481K 215K 1580K 232K 124K + 81K
性能 非常好 Android 下非常好 好 一般 一般
github star 9932 3084 9523 2911
github fork 3377 781 2163 220
支持 JSONPath 支持 不支持 不支持 不支持
支持泛型 支持 支持 支持 支持循环引用 支持 不支持 不支持
不支持
不支持
为了无法计算的价值
5. json/json-lib/databind
71969
19853
java-built-in
23279
5046
5449
4105
bson/jackson/databind
json/gson/databind 4403
4667
hessian 4622
2842
json/jackson/databind
thrift
1866
1164
731
1455
1241
1058
json/fastjson/databind
protobuf
719
1173
1052
796
msgpack/databind
json-array/fastjson/databind
696
650
806
526
json/dsl-platform
0
10000
20000
序列化
30000
反序列化
40000
50000
60000
70000
80000
6. 1800
1580
1600
1400
1200
1000
800
600
481
400
232 205
gson
214
moshi
200
0
fastjson
fastjson-android
jackson
最新版大小 (K)K)
12000
10000
9916
9506
8000
6000
4000
3070
2911
2000
0
fastjson
jackson
github star
gson
github fork
moshi
7. Android 环境性能对比
• 测试环境 - 华为 P10 (低端手机环境低端手机环境 fastjson 会更有优势)
• 数值为耗时,单位毫秒,越小越好
• 内置 org.json 在某些机型下性能会较好,但都不如 fastjson
• 首次序列化 / 反序列化在 fastjson 每次 new ParserConfig/SerializeConfig , jackson 每次 new ObjectMapper , gson 每次 new Gson
fastjson-1.1.60.android gson-2.8.1 内置 org.json 首次序列化 10000 次 1204 2925 14253
首次反序列化 10000 次 2436 3284 20475
序列化 100000 次 1245 4822 2551 1864
反序列化 100000 次 1672 3179 3441 2765
TradeObjectParse 26k1000 次 956 985 1982 1161
CartObjectParse-70k1000 次 2375
2034
5039
在 Android 下绝对优势碾压其他 JSON 库,针对首次优化对首屏快速展示效果非常好
moshi-1.5.0 jackson-2.8.9
2449
为了无法计算的价值
8. 首次序列化 10000 次
首次反序列化 10000 次
16000
25000
14000
20000
12000
10000
15000
8000
10000
6000
4000
5000
2000
0
fastjson-1.1.60.android
gson-2.8.1
0
jackson-2.8.9
fastjson-1.1.60.android
gson-2.8.1
jackson-2.8.9
反序列化 100000 次
序列化 100000 次
4000
6000
3500
5000 3000
4000 2500
2000
3000
1500
2000
1000
1000
0
500
fastjson-1.1.60.android
gson-2.8.1
moshi-1.5.0
jackson-2.8.9
0
fastjson-1.1.60.android
gson-2.8.1
moshi-1.5.0
jackson-2.8.9
CartObjectParse-70k1000 次
TradeObjectParse 26k1000 次
2500 6000
2000 5000
4000
1500
3000
1000
2000
500
0
1000
fastjson-1.1.60.android
gson-2.8.1
moshi-1.5.0
jackson-2.8.9
0
fastjson-1.1.60.android
gson-2.8.1
内置 org.json
jackson-2.8.9
9. FASTJSON 的优点
功能完备
性能最好
支持泛型、 JDK8
第三方评测性能最好
支持 JSONPath
Android 4/5/6/7 下性能最好
活跃受欢迎
要想写什
么就写什
么吧!
稳定
Github 上同类产品 Star 最多 回归测试 3869 个
多年开源中国最受欢迎开源软件评比在前 10 测试覆盖率 86%
开源在 github 上 https://github.com/alibaba/fastjson
Maven 仓库下载地址 http://repo1.maven.org/maven2/com/alibaba/fastjson/
为了无法计算的价值
10. 1/3
性能优化技术
1
2
3
使用 ThreadLocal
在 SerializeWriter/JSONScanner 中使用 ThreadLocal 保存一
个 byte[]/char[] ,减少内存分配
SymbolTable
一个特别优化过的 HashMap ,将常用的 Name 保存起来,不
用每次分配
IdentityHashMap
能避免并发导致的死锁和正确性问题的 IdentityHashMap
4
5
6
缓存 Method/Field/Constructor
在 ParserConfig/SerializeConfig 中保存 Class/Method/Field/
Constructor ,降低反射开销
针对 int/long 序列化的优化
针对 int/long 类型序列化做特别优化,
避免 toString+WriteString 减少对象分配
针对 int/long 反序列化的优化
参考自 Integer.parseInt
Integer.parseInt 不支持输入为 char[] ,支持 String 类型参数
针对 10 进制优化
为了无法计算的价值
11. 2/3
性能优化技术
7
8
9
针对 float/double 反序列化的优化
Float/Doubble.parse 方法很慢
VR 场景有大量的 float/double
encodeUTF8/decodeUTF8 优化
参考 sun.nio.cs.ArrayEncoder/ sun.nio.cs.ArrayDecoder
使用 asm 动态生成 serializer/deserializer
内置 asm ,基于 objectweb asm 3.3 改造,只保留必要部分,
不到 2000 行代码
10
11
12
小方法手动内联
Android 方法调用开销大
Oracle HotSpot 的内联效果也不够好
直接访问 Field 比访问 getter/setter 更好
快速匹配算法
Fastjson 独创的提升 json 反序列化性能的算法
基于字节码动态生成实现
性能超越 jackson 的关键
fnv_hash 匹配优化
来自 json/dsl-platform 的优化算法
用于 android 版本性能优化
为了无法计算的价值
12. 3/3
性能优化技术
13
14
15
BeanToArray 超强性能模式
超越大多数二进制协议
兼顾性能和可维护性
Enum Parse 优化
避免 Name 对象创建
字符串遍历优化
兼顾 HotSpot 和 Android Dalvik 的最优解法
16
17
18
BitFlags
一个 Int 表示 32 个选项
结合 Enum 的 ordinal
JVM Instrinstic
利用 JVM Intrinsic 方法优化
日期类型 Parse 优化
SimpleDateFormat 线程并不安全
SimpleDateFormat 一次只能匹配一种格式
为了无法计算的价值
13. public class SerializeWriter {
static ThreadLocal<char[]> bufLocal = new ThreadLocal<char[]>();
char[] buf;
public SerializeWriter() {
buf = bufLocal.get();
if (buf != null)
bufLocal.set(null);
else
buf = new char[2048];
}
void close() {
if (buf.length <= 1024 * 64)
bufLocal.set(buf);
}
ThreadLocal
1. 通过 bufLocal 重用 char[]
2. 处理一个线程同时多个 SerializeWriter 的问题
1. close 时返还到 bufLocal
2. 控制大小避免 bufLocal 过大
1
void expandCapacity(int minimumCapacity) {
int newCapacity = buf.length + (buf.length >> 1) + 1;
if (newCapacity < minimumCapacity) newCapacity = minimumCapacity;
buf = Arrays.copyOf(buf, newCapacity);
}
}
// com.alibaba.fastjson.serializer.SerializeWriter
为了无法计算的价值
14. public class SymbolTable {
final String[] symbols;
final int indexMask;
1.
2.
3.
4.
用于保存常用的 Name ,减少对象创建
相同 hash 分桶的 symbol 只保留一份
在 char[] 直接和已存的 symbol 比较
预设必须存在的 symbol
public SymbolTable(int tableSize) {
this.indexMask = tableSize - 1;
this.symbols = new String[tableSize];
this.addSymbol("$ref", 0, 4, "$ref".hashCode());
this.addSymbol("@type", 0, 5, ”@type".hashCode());
}
public String addSymbol(char[] buffer, int offset, int len, int hash) {
String symbol = symbols[hash & indexMask];
if (symbol != null) {
if (hash == symbol.hashCode() && len == symbol.length()) {
for (int i = 0; i < len; i++)
if (buffer[offset + i] != symbol.charAt(i))
return new String(buffer, offset, len);
}
return symbol;
}
return symbols[hash & indexMask]
= new String(buffer, offset, len).intern();
}
} // com.alibaba.fastjson.parser.SymbolTable
SymbolTable
2
为了无法计算的价值
15. public class IdentityHashMap<K, V> {
final Entry<K, V>[] buckets
final int indexMask;
1. 基于 System.identityHashCode
2. 不支持 resize 避免并发导致死循环
3. 特别处理避免锁缓存丢失不影响正确性
Identity
HashMap
public IdentityHashMap (int tableSize){
this.indexMask = tableSize - 1;
this.symbols = new Entry[tableSize];
}
public
int
int
for
boolean put(K key, V value) {
hash = System.identityHashCode(key);
bucket = hash & indexMask;
(Entry e = buckets[bucket]; e != null; e = entry.next) {
if (key == e.key) {
e.value = value;
return true;
}
3
}
Entry<K, V> e = new Entry<K, V>(key, value, hash, buckets[bucket]);
buckets[bucket] = e; // 并发是处理时会可能导致缓存丢失,但不影响正确性
return false;
}
} // com.alibaba.fastjson.util.IdentityHashMap
为了无法计算的价值
16. public class JavaBeanInfo {
final Class clazz;
final Constructor defaultConstructor;
final FieldInfo[] fields;
}
1. getField/getMethod 的开销远大于反射调用
2. 缓存 Class 避免 ClassLoader.findClass 开销
public class FieldInfo {
3. 缓存 Constructor/Method/Field 减低反射开销
public final String name;
4. 缓存 Annotation 等信息减少访问元数据 API
public final Method method;
public final Field field;
public final Class declaringClass
public final JSONField fieldAnnotation;
public final JSONField methodAnnotation;
public Object get(Object object) {
return method != null
? method.invoke(object)
: field.get(object);
}
降低
Reflect 开销
4
}
// com.alibaba.fastjson.util.JavaBeanInfo
// com.alibaba.fastjson.util.FieldInfo
为了无法计算的价值
17. // com.alibaba.fastjson.util.IOUtils#stringSize(long)
static int stringSize(long x) {
long p = 10;
for (int i = 1; i < 19; i++) {
if (x < p) return i;
p = 10 * p;
}
1. int/long 的序列化都采用相同的算法
return 19;
2. 比 toString + writeString 相比减少对象创建
}
3. float/double 是基于 toString+WriteString 实现较慢
// com.alibaba.fastjson.util.IOUtils#getChars(long, int, char[])
// com.alibaba.fastjson.util.IOUtils#getChars(int, int, char[])
static void getChars(long i, int index, char[] buf) {
... ...
}
int/long
序列化优化
5
// 在序列化这样使用
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] chars = new char[size];
getChars(i, size, chars);
为了无法计算的价值
18. //
//
//
//
com.alibaba.fastjson.parser.JSONScanner#scanInt
com.alibaba.fastjson.parser.JSONScanner#scanLong
com.alibaba.fastjson.parser.JSONScanner#scanFieldInt
com.alibaba.fastjson.parser.JSONScanner#scanFieldLong
int value;
if (ch >= ‘0’ && ch <= ‘9’) {
value = ch - ‘0’;
for (;;) {
ch = charAt(offset++);
if (ch >= ‘0’ && ch<= ‘9’) {
value = value * 10 + (ch - ‘0’);
} else if (ch == ‘.’) {
// error handle
1. 参考自 Integer.parseInt
} else {
2. Integer.parseInt 不支持输入为 char[]
break;
3. 针对 10 进制优化
}
}
}
int->char[]
Parse 优化
6
为了无法计算的价值
19. int intVal = ch - ‘0’, power = 1;
for (;;) {
ch = charAt(bp + (offset++));
if (ch >= ‘0’ && ch <= ‘9’) {
intVal = intVal * 10 + (ch - ‘0’);
continue;
1. Float.parseFloat 输入需要 String
} else break;
2. Float.parseFloat 和 Double.parseDouble 很慢
}
3. 先当 int 处理,最后再除小数位得到 float
if (ch == ‘.’) {
4. 在 android 某些机型上能得到数十倍的性能提升
ch = charAt(bp + (offset++)); 5. VR 相关的场景有大量的 float
if (ch >= ‘0’ && ch <= ‘9’) {
intVal = intVal * 10 + (chLocal - ‘0’);
for (power = 10; ;power *= 10) {
ch = charAt(bp + (offset++));
if (ch >= ‘0’ && ch <= ‘9’) {
intVal = intVal * 10 + (ch - ‘0’);
continue;
} else break;
}
}
}
value = ((float) intVal) / power;
if (negative)
value = -value;
float/double
Parse 优化
7
为了无法计算的价值
20. package sun.nio;
public class CharsetEncoder {
ByteBuffer encode(CharBuffer in);
}
UTF8Encode
优化
package com.alibaba.fastjson;
public class IOUtils {
static int encodeUTF8(char[] chars, int offset, int len, byte[] bytes);
}
1.
2.
3.
4.
5.
参考自 sun.nio.cs.ArrayEncoder
英文场景能快 25%
中文场景能快 100%
数组直接操作比 CharBuffer 速度快
sun.nio.cs.ArrayDecoder 在 Java8 英文场景性能不如 new String(K)byte, charset)
8
为了无法计算的价值
21. // 反序列化实现
public class ASMDeserializerFactory {
public ObjectDeserializer createJavaBeanDeserializer() {
ClassWriter cw = new ClassWriter();
MethodVisitor mw = new MethodWriter(cw, ACC_PUBLIC, "deserialze”);
… …
mw.visitVarInsn(ALOAD, context.var(“lexer”));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase
, "scanString", "(C)Ljava/lang/String;");
... ...
byte[] code = cw.toByteArray();
Class deserClass = classLoader.defineClassPublic(code);
return deserClass.newInstance();
}
} // com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory
// com.alibaba.fastjson.serializer.ASMSerializerFactory
// 序列化实现
1. 内置一个 asm 实现
public class ASMSerializerFactory {
2. 基于 ObjectWeb ASM 3.3.1 裁剪
}
3. 不到 2000 行
4. 无依赖,体积小
5. 无反射开销
6. 产生式编程能减少分支判断
ASM 动态字
节码优化
9
为了无法计算的价值
22. 小方法内联
public class Model {
public final int value;
public Model(int value) {
this.value = value;
}
}
public class Model {
private int value;
public Model(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
1.
2.
3.
4.
5.
6.
Android 下方法调用的开销较大
Android 下有方法数量 65535 的限制
无论 Android Dalvik 或者 HotSpot 方法调用都有开销
小方法手动内联是一个优化技巧,提升性能,减少体积
public final Field 代替只读 getter
public Field 代替可读写的 getter & setter
10
为了无法计算的价值
23. {”id”:1001,”name”:”wenshao”}
public class Person {
public int id;
public String name;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
Name 占据 JSON 字符串相当大的一部分
通过匹配而不是把 Name 读取出来性能大幅提升
读写按照相同的顺序匹配成功率会更高
缺失部分字段不会影响匹配
不符合顺序走普通模式
快速匹配算法合适使用 ASM 动态字节码实现
Name 匹配可以用 SIMD 指令优化
这个算法是性能超越 Jackson 的关键
快速匹配算法不适合用于 Android
Person p = new Persson();
// 先挨个 Name 做匹配
if (matchField(“id”)) p.id = readInt();
if (matchField(“name”)) p.name = readString();
// 剩下不匹配的走常规模式
String name = readName();
FieldDeserializer fieldDeser = fndFieldDeserializer(name);
if (fieldDeser != null) fieldDeser.readValue();
// charArrayCompare 是 matchField 的关键实现
static boolean charArrayCompare(String src, int offset, char[] dest) {
for (int i = 0; i < destLen; ++i) {
if (dest[i] != src.charAt(offset + i)) return false;
return true;
}
}
快速匹配算法
11
为了无法计算的价值
24. {”id”:1001,”name”:”wenshao”}
1.
2.
3.
4.
5.
6.
快速排序需要动态字节码生成不能用于 Android
算法来自 dsl-platform 和 jsoniter
假设一个对象内不会存在相同 hash 值的 Name
避免了 Name 对象的创建,性能非常好
Android 版本使用 fnv_hash 匹配算法和标准不同
采用 fnv_hash_64 而不是 fnv_hash_32 不同于 jsoniter
public class Person {
public int id;
public String name;
}
long readNameHash() {
long hash = 0x811c9dc5;
for (; i < text.length; ++p) {
char ch = text.charAt(p);
if (ch == '"') break;
hash ^= ch;
hash *= 0x1000193;
}
return hash;
}
long nameHash = readNameHash();
FieldDeserializer fieldDeser = fndFieldDeserializer(nameHash);
if (fieldDeser != null) {
fieldDeser.readValue();
}
// 以上为伪码,真实实现会复杂很多
FNVHash
匹配算法
12
为了无法计算的价值
25. // 文档 https://github.com/alibaba/fastjson/wiki/BeanToArray_cn
class Company {
public int code;
public List<Department> departments = new ArrayList<Department>();
}
@JSONType(serialzeFeatures = SerializerFeature.BeanToArray
, parseFeatures = Feature.SupportArrayToBean)
class Department {
public int id;
public Stirng name;
public Department() {}
public Department(int id, String name) {
this.id = id;
1. 开启 BeanToArray 之后,输出是 JSONArray 结构
this.name = name;
2. 局部开启 BeanToArray 能兼顾性能和可维护性的平衡
}
3. BeanToArray 模式性能超越大多数二进制协议
}
BeanToArray
性能超强模式
13
Company cmpy = new Company();
cmpy.code = 100;
cmpy.departments.add(new Department(1001, "Sales"));
cmpy.departments.add(new Department(1002, "Financial"));
// {"code":10,"departments":[[1001,"Sales"],[1002,"Financial"]]}
String text = JSON.toJSONString(commpany);
为了无法计算的价值
26. public class EnumDeserializer {
1. 构建一个排好序的 hashcode 数组
protected final Enum[] enums;
2. 构建根据 hashCode 排序的 enums
protected final long[] hashCodes;
public EnumDeserializer (Class enumClass) {
this.enums = (Enum[]) enumClass.getEnumConstants();
this.hashCodes = new long[enums.length];
for (int i = 0; i < enums.length; ++i)
hashCodes[i] = fnv_hash_64(enums[i].name);
Arrays.sort(hashCodes);
Arrays.sort(enums, (a, b) -> {
long x = fnv_hash_64(a.name()), y = fnv_hash_64(b.name());
return (x < y) ? -1 : ((x == y) ? 0 : 1)
});
}
public Enum getEnumByHashCode(long hashCode) {
int index = Arrays.binarySearch(this.hashCodes, hashCode);
if (index < 0) return null;
1. 读取 name 的 HashCode
return enums[index];
2. 通过 HashCode 查找 Enum
}
Enum scanEnum() {
long hashCoce = lexer.scanHashCode();
return getEnumByHashCode(hashCode);
}
}
Enum Parse
优化
14
为了无法计算的价值
27. // 方法一
String text = …;
char[] chars = text.toCharArray(); // 这里会导致一次内存分配和拷贝,速度较慢
for (int i = 0; i < chars.length; ++i) {
char ch = chars[i];
}
// 方法二
for (int i = 0; i < text.length(); ++i) {
char ch = text.charAt(i); // Android 下每次调用 length() 方法会有开销
}
// 方法三
for (int i = 0, len = text.length(); i < len; ++i) {
char ch = text.charAt(i);
}
字符串
遍历优化
15
1. Android 下 Dalvik 小方法调用不会内联有开销
2. 方法三是最优解,兼顾 HotSpot 和 Dalvik
为了无法计算的价值
28. public enum Feature {
UseBigDecimal, SortFeidFastMatch, IgnoreNotMatch;
public final int mask;
private Feature() {
mask = (1 << ordinal());
}
}
1.
2.
3.
4.
5.
BitFlags
使用 int 的每一个 bit 标识一个选项
int 只有 32 个 bit 所以只有 32 个选项
通过 & mask != 0 查看是否已设置选项
通过 |= 和 &= 配置选项
ASM 动态代码生成时会根据常用组合做优化
int features = 0;
features |= Feature.UseBigDecimal.mask;
features |= Feature.SortFeidFastMatch.mask;
features |= Feature.IgnoreNotMatch.mask;
public class JSONLexer {
private int features;
public boolean isEnabled(Feature feature) {
return (features & feature.mask) != 0;
}
public void config(Feature feature, boolean state) {
if (state) features |= feature.mask;
else features &= ~feature.mask;
}
}
16
为了无法计算的价值
29. 查看 JVM Intrinsic 的代码路径 src/share/vm/classfile/vmSymbols.hpp
class String {
int compareTo(String);
int indexOf(String);
boolean equals(String);
}
1.
2.
3.
4.
JVM Intrinsic
JVM 提供的 Instrinsic 很少
通常只是 System.arrayCopy 有用
查看是否 Instrinsic 注意参数类型匹配
在阿里关键方法可以提需求给 JVM 团队
class System {
int identityHashCode(Object);
long currentTimeMillis();
long nanoTime();
void arrayCopy(Object, int, Object, int, int);
}
17
class Arrays {
void copyOf(...);
void copyOfRange(...);
boolean equals(...);
}
http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/
vm/classfile/vmSymbols.hpp
为了无法计算的价值
30. class JSONScanner {
public Calendar calendar;
public boolean scanISO8601DateIfMatch() {
char y0 = charAt(bp);
char y1 = charAt(bp + 1);
1. SimpleDateFormat 线程不安全
char y2 = charAt(bp + 2);
2. SimpleDateFormat 一次只能识别一种格式
char y3 = charAt(bp + 3);
3. 一次遍历支持大多数常用 DateFormat 的识别
char M0 = charAt(bp + 4);
char M1 = charAt(bp + 5);
char d0 = charAt(bp + 6);
char d1 = charAt(bp + 7);
if (!checkDate(y0, y1, y2, y3, M0, M1, d0, d1))
return false;
char h0 = charAt(bp + 8);
char h1 = charAt(bp + 9);
char m0 = charAt(bp + 10);
char m1 = charAt(bp + 11);
char s0 = charAt(bp + 12);
char s1 = charAt(bp + 13);
DateParse
优化
18
setCalendar(y0, y1, y2, y3, M0, M1, d0, d1);
... ...
}
} // com.alibaba.fastjson.parser.JSONScanner
为了无法计算的价值
31. No. 2
FASTJSON 优化技术总结
算法
GC 友
好
用好 Hash 算法
减少内存分配
用好简单类型
避免 算法
GC 锁
CP
友好
U友
为特定场景优化的算法
好
连续操作, CacheLine 对齐
批量操作, SIMD 优化
用好数组
CPU 友好
锁的开销会较大
避免锁
为了无法计算的价值
32. 1/3
FASTJSON 相关技术和功能点
1
2
3
TypeReference
用以支持泛型,避免编译擦除
JSONPath
支持求值、修改、统计
可以当做 OQL 使用
和 JSON 解析公用基础设施,保证性能
自定序列化 Filter
各种 SerializerFilter
4
5
6
自定义反序列化
用于支持 MapBean 的 ExtraProcessable
避免 SubString 引用问题
避免 Android 2/34/5 下 subString 引用问题
WriteClassName
自带类型信息
曾有安全漏洞
SeeAlso
为了无法计算的价值
33. Type
Reference
class Model {}
// 例一
List<Model> models = JSON.parseObject(“[{},{},{}]”
,
new TypeReference<List<Model>>(){});
// 例二
Type type = new TypeRefrence<List<Model>>(){}.getType();
List<Model> models = JSON.parseObject(“[{},{},{}]”, type);
// 例三 框架支持
class Response<T> {
public T data;
}
1. 通过内嵌声明一个 TypeReference 派生类获得类型
2. 避免了编译擦除泛型信息问题
3. 例二中的 type 单例化处理性能更好
1
public static <T> Response<T> parseRepsonse(String json, Type type) {
return JSON.parseObject(json, new TypeReference<Response<T>>() {});
}
为了无法计算的价值
34. public class Entity {
public Integer id;
public String name;
public Object value;
public Entity() {}
public Entity(Integer id, Object value) {
this.id = id; this.value = value;
}
public Entity(String name) { this.name = name; }
}
Entity entity = new Entity(123, new Object());
assertSame(entity.value, JSONPath.eval(entity, "$.value"));
assertTrue(JSONPath.contains(entity, "$.value"));
assertTrue(JSONPath.containsValue(entity, "$.id", 123));
assertTrue(JSONPath.containsValue(entity, "$.value", entity.value));
assertEquals(2, JSONPath.size(entity, "$"));
assertEquals(0, JSONPath.size(new Object[0], "$"));
1. 可以通过 JSONPath 求值、修改、统计、判断是否存
在
2. 可将 JSONPath 当做 OQL 使用
3. 和 JSON 解析共用基础设施,性能有保证
https://github.com/alibaba/fastjson/wiki/JSONPath
JSONPath
2
为了无法计算的价值
35. // 根据 PropertyName 和 PropertyValue 来判断是否序列化
public interface PropertyPreFilter extends SerializeFilter {
boolean apply(JSONSerializer serializer, Object obj, String name);
}
// 和 PropertyFilter 不同只根据 object 和 name 进行判断,在调用 getter 之前
// 这样避免了 getter 调用可能存在的异常
public interface PropertyPreFilter extends SerializeFilter {
boolean apply(JSONSerializer serializer, Object obj, String name);
}
// 序列化时修改 Key
public interface NameFilter extends SerializeFilter {
String process(Object obj, String propertyName, Object propertyValue);
}
// 序列化是修改 Value
public interface ValueFilter extends SerializeFilter {
Object process(Object obj, String propertyName, Object propertyValue);
}
// 和 ValueFilter 类似,只是多了 BeanContext 参数可用。
public interface ContextValueFilter extends SerializeFilter {
Object process(BeanContext ctx, Object obj, String name, Object val);
}
// 还有其他的 SerializeFilter LabelFilter SimplePropertyPreFilter
// 注册在 Class 级别
https://github.com/alibaba/fastjson/wiki/Class_Level_SerializeFilter
https://github.com/alibaba/fastjson/wiki/SerializeFilter
自定义
序列化 Filter
3
为了无法计算的价值
36. public class Model implements JSONSerializable, ExtraProcessable {
protected Map<String, Object> attributes
= new HashMap<String, Object>();
自定义
反序列化
public Map<String, Object> getAttributes() { return attributes;}
public Object getAttribute(String name) { return
attributes.get(name); }
public void write(JSONSerializer serializer
, Object fieldName
, Type fieldType
, int features) throws IOException {
serializer.write(attributes); // 定制序列化
}
4
public void processExtra(String key, Object value) {
attributes.put(key, value); // 定制反序列化
}
}
1. ExtraProcessable 用于不匹配类型的序列化,可用于使用 MapBean 的框架
2. 类似功能的还有 PropertyProcessable
https://github.com/alibaba/fastjson/wiki/PropertyProcessable_cn
https://github.com/alibaba/fastjson/wiki/ExtraProcessable
为了无法计算的价值
37. JDK 1.4/5/6 & Android 2/3/4/5 subString 返回的字符串会持有原字符串 char[] 引用
static boolean V6; // android 6
static {
int version = -1;
try {
Class<?> clazz = Class.forName("android.os.Build$VERSION");
Field field = clazz.getField("SDK_INT");
version = field.getInt(null);
} catch (Exception e) {
// skip
}
V6 = version >= 23;
}
避免
SubString
引用问题
5
// subString 处理
if (V6) {
strVal = text.substring(startIndex, endIndex);
} else {
int chars_len = endIndex - startIndex;
char[] chars = sub_chars(bp + offset, chars_len);
strVal = new String(chars, 0, chars_len);
}
为了无法计算的价值
38. public class Model {
public int id;
public String name;
public Entity() {}
public Entity(int id, String
this.id = id;
this.name = name;
}
}
1. 这个功能在 1.2.24 之前有安全漏洞
2. 1.2.25 之后会有安全保护
3. 自带类型信息使得 json 相当于 Java 的序列化
Write
ClassName
name) {
Model model = new Model(3, "wenshao");
String text = JSON.toJSONString(model, SerializerFeature.WriteClassName);
// 如下是 toJSONString 返回的 text , @type 是类型信息
// {"@type":"com.alibaba.json.demo.Model","id":3,"name":”wenshao"}
6
Model model2 = (Model) JSON.parse(text);
assertEquals(model.id, model2.id);
assertEquals(model.name, model2.name);
支持类似 JAXB 的 XmlSeeAlso 功能
https://github.com/alibaba/fastjson/wiki/JSONType_seeAlso_cn
安全升级公告
为了无法计算的价值
39.