GSON源码分析之适配器模式及自定义适配器Adapter
FastJson最近频频爆出严重漏洞,所以决定长痛不如短痛,把已有项目的FastJson全部替换成GSON。因为要尽量要避免改动业务代码,所以必须对GSON做深入研究,与FastJson比对并解决序列化与反序列化差异问题。因为差异较多,此文主要分析一下适配器源码,及其自定义适配器Adapter。
源码分析
1:入口GSON.fromJson(JsonReader reader, Type typeOfT):
2:分析获取getAdapter方法:
/**
* Returns the type adapter for {@code} type.
* 此方法获取目标类型全部字段所对应的适配器,对象内部都是使用递归的方式获取
* @throws IllegalArgumentException if this GSON cannot serialize and
* deserialize {@code type}.
*/
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
// 尝试从GSON缓存中获取Adapter
TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
if (cached != null) {
return (TypeAdapter<T>) cached;
}
//从ThreadLocal获取FutureTypeAdapter
//FutureTypeAdapter是TypeAdapter的一个代理模式的包装类,可以看作就是TypeAdapter
Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
//是否需要清理ThreadLocal
boolean requiresThreadLocalCleanup = false;
//首次进入此方法创建Map,并标记使用完成清理ThreadLocal
if (threadCalls == null) {
threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
calls.set(threadCalls);
requiresThreadLocalCleanup = true;
}
// the key and value type parameters always agree
//尝试从ThreadLocal中获取已创建好的Adapter,此处主要为递归时准备,可以避免栈溢出问题
FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
}
try {
//创建FutureTypeAdapter
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
//加入ThreadLocal
threadCalls.put(type, call);
//遍历GSON已注册的所有Adapter
//我们自定义的Adapter就是注册到factories的
for (TypeAdapterFactory factory : factories) {
//尝试匹配每个适配器,此方法内部递归调用getAdapter()
//
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
//匹配成功的适配器赋值到ThreadLocal
call.setDelegate(candidate);
//加入缓存
typeTokenCache.put(type, candidate);
return candidate;
}
}
throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
} finally {
//每次从ThreadLocal移除已匹配到的适配器
threadCalls.remove(type);
//当已经创建完所有适配器的时候,释放ThreadLocal
if (requiresThreadLocalCleanup) {
calls.remove();
}
}
}
3:getAdapter的递归调用链:
getAdapter(TypeToken<T> type) ->
ReflectiveTypeAdapterFactory.create(Gson gson, final TypeToken<T> type) ->
ReflectiveTypeAdapterFactory.getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) ->
ReflectiveTypeAdapterFactory.createBoundField( final Gson context, final Field field, final String name,final TypeToken<?> fieldType, boolean serialize, boolean deserialize) ->
getAdapter(TypeToken<T> type)
4:分析
代码考虑到多线程问题,所以使用ThreadLocal在主线程中进行缓存,单次结束后移出ThreadLocal,这也节约了其他线程获取同类型适配器创建适配器所需消耗的资源。
还有一个设计巧妙的地方,也就是使用包装类的意义:
自定义Adapter:
1:继承抽象类TypeAdapter,实现read()和write()方法,例如时间适配器。
package com.tencent.trip.util.gson;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Locale;
/**
* @Description: 时间适配器
* @Author:
* @Date: 10:18 2020/4/27
*/
public class CustDateTypeAdapter extends TypeAdapter<Date> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
@SuppressWarnings("unchecked")
TypeAdapter<T> typeAdapter = (TypeAdapter<T>) ((typeToken.getRawType() == Date.class) ? new CustDateTypeAdapter()
: null);
return typeAdapter;
}
};
private final DateFormat zhCNFormat;
private final DateFormat dateFormat;
public CustDateTypeAdapter() {
this.zhCNFormat = DateFormat.getDateTimeInstance(2, 2, Locale.SIMPLIFIED_CHINESE);
this.dateFormat = DateFormat.getDateInstance(2, Locale.SIMPLIFIED_CHINESE);
}
@Override
public Date read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return deserializeToDate(in.nextString());
}
private synchronized Date deserializeToDate(String json) {
try {
return new Date(Long.parseLong(json));
} catch (Exception e) {
try {
return this.zhCNFormat.parse(json);
} catch (ParseException e1) {
try {
return this.dateFormat.parse(json);
} catch (ParseException e2) {
throw new JsonSyntaxException(json, e2);
}
}
}
}
@Override
public synchronized void write(JsonWriter out, Date value)
throws IOException {
if (value == null) {
out.nullValue();
return;
}
String dateFormatAsString = this.zhCNFormat.format(value);
out.value(dateFormatAsString);
}
}
2:注册到GSON:
//注册适配器日期类型
Gson GSON = new GsonBuilder().registerTypeAdapter(Date.class, new CustDateTypeAdapter())
.create();