摘要
Gson源码设计学习
一、概述
在Java、Android相关的开发中,经常会用到Json自动解析框架,其中比较常见的一个就是Google推出的Gson。
关于Gson的使用,这个系列文章已经讲的很全面了,就不重复写了。
你真的会用Gson吗?Gson使用指南
http://www.jianshu.com/p/e740196225a4
Gson源码分析的文章相对较少。本文尝试对Gson源码进行简明且较全面的分析,重点关注其中的设计思想。基于Gson 2.8.0,建议结合源码阅读。
Google / Gson – GitHub
https://github.com/google/gson
文章开始前,先简单回顾一下Gson基本用法。
Gson gson = new Gson();
// 解析数据(使用Class)
String json1 = "{\"key\":\"value\"}";
Data data1 = gson.fromJson(json, Data.class);
// 解析带泛型的数据(使用TypeToken)
String json2 = "[{\"key\",\"value1\"},{\"key\",\"value2\"}]";
List<Data> list = gson.fromJson(json2, new TypeToken<List<Data>>(){}.getType());
// 序列化数据
Data data3 = new Data();
String json3 = gson.toJson(data3);
二、主要接口
TypeToken:类型处理工具类
Gson解析数据时要知道Java对象的类型(包括其泛型),TypeToken工具类可以方便的处理类型。
例如给Gson传一个List<String>
类型的Type对象,可以用下面的方式。TypeToken提供了protected权限的构造函数,通过继承一个匿名类即可实例化,getType返回一个ParameterizedType实例。
Type type = new TypeToken<List<String>>(){}.getType();
Type是Java中的接口,表示对象类型。Type有一个实现类
Class
(普通类型,例如Object
、ArrayList
)和几个子类接口:
- GenericArrayType(数组类型,例如
String[]
)- ParameterizedType(泛型类型,例如
List<String>
)- WildcardType(形如
? extends ClassA
、?super ClassB
)- TypeVariable(类型变量)
参考:Java Type详解
http://blog.csdn.net/gdutxiaoxu/article/details/68926515
JsonToken:Json流式操作API
Gson封装了一组偏底层的JsonToken API。JsonToken是一个枚举类型,每种类型表示一个原始Json元素,例如:
- BEGIN_ARRAY:对应
[
- BEGIN_OBJECT:对应
{
- NULL:对应
null
public enum JsonToken {
BEGIN_ARRAY,
END_ARRAY,
BEGIN_OBJECT,
END_OBJECT,
NAME,
STRING,
NUMBER,
BOOLEAN,
NULL,
END_DOCUMENT
}
JsonReader、JsonWriter:JsonString转JsonToken
JsonReader和JsonWriter用于读写Json字符串,在Json字符串和JsonToken之间转换。
JsonToken、JsonReader、JsonReader通过数据流的形式操作Json,都放在包com.google.gson.stream
中,也称为Stream API。
JsonElement:Json元素树
Gson还封装了一组JsonElement API。抽象类JsonElement表示一个Json元素,每个JsonElement可包含0到多个子JsonElement,形成树结构。JsonElement的子类有:
- JsonPrimitive:Json基本类型,例如
1
,"text"
,true
- JsonObject:Json对象,例如
{"key", "val"}
- JsonArray:Json数组,例如
[{"key", "val1"}, {"key", "val2"}]
- JsonNull:Json Null元素,即
null
JsonSerializer、JsonDeserializer:JsonElement转JavaObject
JsonSerializer和JsonDeserializer用于实现JsonElement树到Java对象之间的转换。
public interface JsonSerializer<T> {
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
}
public interface JsonDeserializer<T> {
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException;
}
TypeAdapter:JsonToken转JavaObject
相关背景
早期的Gson例如1.6版本中,Json数据先通过JsonReader/JsonWriter转换到JsonToken流,再通过Streams工具类转换到JsonElement树,最后由JsonSerializer/JsonDeserializer转换到Java对象。
Gson在2.0版本中引入了TypeAdapter,并在2.1版本开放接口。TypeAdapter可以直接转换JsonToken流和Java对象,可以不经过JsonElement,性能得到了提高。
Version 2.0
Previous versions first parsed complete document into a DOM-style model (JsonObject or JsonArray) and then bound data against that. Gson 2 does data binding directly from the stream parser.
Version 2.1
Support for user-defined streaming type adapters
接口定义
TypeAdapter是一个抽象类,除了一些共通方法,还包含了两个抽象方法:
- write:序列化,Java对象 –> JsonToken –> JsonWriter
- read:反序列化,JsonReader –> JsonToken –> Java对象
public abstract class TypeAdapter<T> {
public abstract void write(JsonWriter out, T value) throws IOException;
public abstract T read(JsonReader in) throws IOException;
// ...
}
TypeAdapter与Serializer对比
TypeAdapter | JsonSerializer、JsonDeserializer | |
---|---|---|
引入版本 | 2.0 | 1.x |
Stream API | 支持 | 不支持,要先生成JsonElement |
内存占用 | 较小 | 较大 |
效率 | 较高 | 较低 |
作用范围 | 序列化 和 反序列化 | 序列化 或 反序列化 |
TypeAdapters
TypeAdapters类中包含了一些基本类型的TypeAdapter实现,具体可以自行阅读源码。
TypeAdapterFactory:创建TypeAdapter
TypeAdapterFactory用于创建TypeAdapter。传入特定的type,Factory返回相应的TypeAdapter实例。如果不支持这种类型,则返回null。
public interface TypeAdapterFactory {
<T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
}
Gson、GsonBuilder
通常使用Gson对象来序列化/反序列化Json数据。
Gson对象中包含了一系列的配置属性。对于常规简单情况,可以直接用new Gson()
方式创建一个全部使用默认配置的Gson实例。如果需要自定义各种配置,则可使用GsonBuilder创建。
Gson对象内部是线程安全的,因此创建了一个Gson实例后,可以在多线程之间反复使用。
// Gson工具类封装
public class GsonUtils {
private static final Gson gson;
static {
gson = new GsonBuilder()
// ...
.create();
}
public static Gson getGson() {
return gson;
}
public static <T> T fromJson(String json, Class<T> clazz) {
return gson.fromJson(json, clazz);
}
public static <T> String toJson(T obj) {
return gson.toJson(obj);
}
}
三、Gson设计学习
解析时Java对象的创建
在解析数据时,会给Gson传入Java对象的Type。Gson需要从Type创建对象实例,其中绝大多数对象都是通过ConstructorConstructor工具类创建的。
- 大部分类型的实例创建,直接通过反射调用默认无参构造函数。
- 对于List、Map、Set等接口类型,创建默认实现类ArrayList、HashMap、HashSet等的对象。
- 对于没有默认构造函数的其他类型,可以向Gson注册InstanceCreator自己创建。
- Primitive及封装类型、数组类型等,在相应的TypeAdapter中自己创建,不调用ConstructorConstructor。
public class User {
private final Context context;
private long id;
private String name;
public User(Context context) {
this.context = context;
}
// ...
}
public class UserInstanceCreator implements InstanceCreator<User> {
@Override
public User createInstance(Type type) {
return new User(ContextProvider.getDefaultContext());
}
}
Gson gson = new GsonBuilder()
.registerTypeAdapter(User.class, new UserInstanceCreator())
.create();
TypeAdapter获取过程
Gson在序列化/反序列化前,都需要先知道Java对象的Type,获取到对应的TypeAdapter,再进行操作。
Gson.getAdapter()方法可以根据传入的TypeToken,返回对应的TypeAdapter。下面对其代码进行分析。
TypeAdapter缓存
Gson有个ConcurrentHashMap缓存,先从缓存取已经生成了的TypeAdapter。
private final Map<TypeToken<?>, TypeAdapter<?>> typeTokenCache = new ConcurrentHashMap<TypeToken<?>, TypeAdapter<?>>();
TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
if (cached != null) {
return (TypeAdapter<T>) cached;
}
遍历TypeAdapterFactory创建TypeAdapter
Gson中还有一个List<TypeAdapterFactory>
,按优先级保存了若干TypeAdapterFactory。
如果上一步TypeAdapter没有缓存,则需要创建。Gson依次遍历每个TypeAdapterFactory并调用create,直到有一个Factory返回了非空的TypeAdapter对象。
private final List<TypeAdapterFactory> factories;
for (TypeAdapterFactory factory : factories) {
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
call.setDelegate(candidate);
typeTokenCache.put(type, candidate);
return candidate;
}
}
递归调用问题的解决
Factory在创建Adapter时,可能会嵌套递归调用Gson.getAdapter
方法导致死循环、堆栈溢出。为了解决这个问题,getAdapter在调用Factory前会先保存一个空的FutureTypeAdapter到ThreadLocal中;之后Factory再嵌套调用getAdapter时取到的是FutureTypeAdapter;当获取到最终的Adapter后再设置到这个FutureTypeAdapter中。
This thread local guards against reentrant calls to getAdapter(). In certain object graphs, creating an adapter for a type may recursively require an adapter for the same type! Without intervention, the recursive lookup would stack overflow. We cheat by returning a proxy type adapter. The proxy is wired up once the initial adapter has been created.
DelegateAdapter设计:拦截器效果
Gson中有些TypeAdapter会有类似网络框架拦截器的效果(Interceptor),例如只在部分情况处理序列化/反序列化,或插入一些操作,剩下的再交给其他匹配的TypeAdapter处理。
为了实现这种效果,Gson中设计了DelegateAdapter机制。通过调用Gson.getDelegateAdapter方法并传入跳过的Factory,Gson会遍历优先级更低的Factory创建DelegateAdapter。
// skipPast即为跳过的Factory,遍历比skipPast优先级更低的Factory创建DelegateAdapter
public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type);
如下就是DelegateAdapter的一个应用,可以统计Gson序列化/反序列化了多少对象。
class StatsTypeAdapterFactory implements TypeAdapterFactory {
public int numReads = 0;
public int numWrites = 0;
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
++numWrites;
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
++numReads;
return delegate.read(in);
}
};
}
}
自定义TypeAdapter/TypeAdapterFactory
当需要自己处理特定数据类型时,可以用GsonBuilder配置生成Gson对象,如下:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Test1.class, new Test1Adapter())
.registerTypeHierarchyAdapter(Test2.class, new Test2Deserializer())
.registerTypeAdapterFactory(new Test3AdapterFactory())
.create();
其中:
- registerTypeAdapter,可传入JsonSerializer、JsonDeserializer、InstanceCreator、TypeAdapter,处理特定类型数据
- registerTypeHierarchyAdapter,可传入JsonSerializer、JsonDeserializer、TypeAdapter,处理特定类型及其子类数据
- registerTypeAdapterFactory,注册自定义的Factory
Builder中注册的JsonSerializer/JsonDeserializer/TypeAdapter都会被转换成TypeAdapterFactory,最终加入到Gson的Factory列表中。
自定义的Factory,比Gson内置的大部分Factory优先级高(除了个别特殊的内置Factory)。
TypeAdapters.JSON_ELEMENT:JsonToken转JsonElement
JSON_ELEMENT的作用是转换JsonToken和JsonElement。
有时需要自己序列化/反序列化Java对象,除了自定义TypeAdapter/Serializer,还可以直接让Gson将Json字符串转换到JsonElement树,之后再处理。
例如某个网络请求,不同的code对应不同的data结构,可以直接把data定义为JsonElement类型,由Gson解析成JsonElement树,之后再根据code在业务代码中处理。
{
"code": 0,
"data": {
"key": "value"
}
}
{
"code": 1,
"data": "msg"
}
class Response {
int code;
JsonElement data;
}
String json = "...";
Response response = new Gson().fromJson(json, Response.class);
if (response != null) {
switch (response.code) {
case 0: // ...
break;
case 1: // ...
break;
}
}
TreeTypeAdapter
前面提到,在Gson 1.x中只有JsonSerializer/JsonDeserializer,2.0才开始引入TypeAdapter。TreeTypeAdapter就是适配Serializer而设计的。
一方面TreeTypeAdapter将JsonToken转换到JsonElement树,再调用Serializer,这也是其名称中“Tree”的由来;另一方面TreeTypeAdapter支持只设置JsonSerializer或JsonDeserializer之一,另一个则通过调用DelegateAdapter来处理。
TreeTypeAdapter.read方法如下(write方法类似),如果有Deserializer则转换到JsonElement并使用Deserializer,否则调用DelegateAdapter处理。
public final class TreeTypeAdapter<T> extends TypeAdapter<T> {
@Override
public T read(JsonReader in) throws IOException {
if (deserializer == null) {
return delegate().read(in);
}
JsonElement value = Streams.parse(in);
if (value.isJsonNull()) {
return null;
}
return deserializer.deserialize(value, typeToken.getType(), context);
}
private TypeAdapter<T> delegate() {
TypeAdapter<T> d = delegate;
return d != null ? d : (delegate = gson.getDelegateAdapter(skipPast, typeToken));
}
// ...
}
JsonAdapter注解:设置自定义TypeAdapter
给数据类型自定义TypeAdapter/TypeAdapterFactory或JsonSerializer/JsonDeserializer,除了用GsonBuilder配置,也可以直接用JsonAdapter注解。这个注解可作用于类和成员变量。
@JsonAdapter(UserDeserializer.class)
class User {
@JsonAdapter(IdTypeAdapter.class)
public long id;
public String name;
}
JsonAdapter的实现在JsonAdapterAnnotationTypeAdapterFactory中,这个Factory的create方法会判断如果Type有JsonAdapter注解,则根据注解创建对应的Adapter/Serializer处理。
需要注意的是,由于JsonAdapterAnnotationTypeAdapterFactory在Gson中的优先级很低(具体可以看Gson构造函数源码),因此JsonAdapter注解修饰绝大多数Gson原生支持数据类型时是无效的,包括Primitive和封装类型(int/Integer/long/Long…)、String、数组、Collection及Map的子类等。
ReflectiveTypeAdapterFactory:反射解析Java对象
ReflectiveTypeAdapterFactory是Gson中优先级最低、但使用很频繁的Factory,前面所有Factory都没有处理的Type,最终都会由这个Factory创建Adapter来处理。
ReflectiveTypeAdapterFactory.Adapter通过反射读取Java对象的每个Field,然后根据相应的名字和类型,再调用其他匹配的Adapter来处理。
FieldNamingStrategy与SerializedName注解:字段名映射配置
ReflectiveTypeAdapterFactory.Adapter读取Java对象的Field,默认对应到Json中的字段名就是FieldName,但也可以改变这个规则。
通过GsonBuilder.setFieldNamingStrategy,可以设置FieldNamingStrategy,按照一定的规则转换命名风格。例如设置为
FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES
,则Java字段someFieldName
对应Json中的some_field_name
字段。通过SerializedName注解,可以指定Field对应的Json字段名,还可以用alternate指定解析时尝试多个候选项。
public class MyClass { @SerializedName("name") String a; @SerializedName(value="name1", alternate={"name2", "name3"}) String b; String c; }
Expose注解:字段过滤
ReflectiveTypeAdapterFactory.Adapter默认处理Java对象的每个Field(static和transient成员除外),也可以用Expose注解配置。
- Expose注解有serialize、deserialize两个字段,默认均为true,设置为false则不处理这个字段
- 没有Expose注解的字段默认均处理,如果设置了GsonBuilder.excludeFieldsWithoutExposeAnnotation,则跳过所有没添加Expose注解的字段。
public class User {
@Expose
private String firstName;
@Expose(serialize = false)
private String lastName;
@Expose (serialize = false, deserialize = false)
private String emailAddress;
private String password;
}
Excluder、ExclusionStrategy、Since/Until注解:Class和字段过滤
Excluder用于忽略指定的类和Field。
前面已经提到可以使用Expose注解忽略特定字段。此外还可以忽略带有特定修饰符的Field(Modifier)、根据Since/Until注解指定的版本忽略、忽略内部类、使用ExclusionStrategy忽略等。具体用法可参考下文,不再详细介绍。
你真的会用Gson吗?Gson使用指南(三) http://www.jianshu.com/p/0e40a52c0063
实现方面,对Field的忽略,是由ReflectiveTypeAdapterFactory调用Excluder实现的。对于Class的忽略,Excluder自身也是一个TypeAdapterFactory,且优先级高于绝大多数Factory,在Excluder.create方法中判断如果Type的序列化或反序列化需要忽略,则进行相应的拦截处理。
Gson Design Document
在Gson官方的Design Document中,还列举了一些Gson设计过程中遇到的问题,例如解析时如何创建对象实例,为什么Gson中的大部分类都是final的,等等。
https://github.com/google/gson/blob/master/GsonDesignDocument.md
四、参考资料与扩展阅读
通过自定义TypeAdapter对Gson进行封装,解决一些开发中经常遇到的问题,可阅读这篇文章
Gson TypeAdapter使用技巧几例:数据免判空、解析后校验、预处理
https://www.paincker.com/gson-technic
其他参考资料
Google / Gson – GitHub
https://github.com/google/gson
你真的会用Gson吗?Gson使用指南
http://www.jianshu.com/p/e740196225a4
Java Type详解
http://blog.csdn.net/gdutxiaoxu/article/details/68926515
最后,欢迎扫码关注微信公众号。程序员同行学习交流,聊天交友,国内外名企求职内推(微软 / 小冰 / Amazon / Shopee / Coupang / ATM / 头条 / 拼多多等),可加我微信 jzj2015 进技术群(备注进技术群,并简单自我介绍)。

本文由jzj1993原创,转载请注明来源:https://www.paincker.com/gson-study
(标注了原文链接的文章除外)
Gson对象内部是线程安全的?
为什么是线程安全的?
这个可以看代码,Gson内部有针对线程问题做一些处理,例如涉及到多线程同时访问的一些变量,用到了ConcurrentHashMap、ThreadLocal之类线程安全的工具类……