基于非墨上一篇关于泛型的文章,不知道大家是否已经做好了阅读Gson源码的准备?本篇文章,非墨将简单带大家过一下Gson的源码。对于我们来说,Gson其实就是一套模型转换工具,他的一端连接的是json数据格式编码流,另一头就是我们的Java对象模型。我们先给自己找一个入口点,看下Gson一个常见的例子:
public static class ClassRoom {
public String roomName;
public int number;
public String toString() {
return "[" + roomName + ":" + number + "]";
}
}
public static class User {
public String name;
public int age;
private ClassRoom room;
@Override
public String toString() {
// TODO Auto-generated method stub
return name + "->" + age + ":" + room;
}
}
我们尝试传入一个json格式的数据串
Gson gson = new Gson();
String strJson = "{name:'david',age:19,room:{roomName:'small',number:1}}";
User u = gson.fromJson(strJson, User.class);
就这样,通过一个简单的Gson接口,将json码流转换成为一个Java对象。为了我们开发的方便,一般这类对象我们选择JavaBean格式的对象模型。
在上述代码中,gson相当于起到了一个数据适配器的作用,将一个Json的数据格式转换为一个Java业务可用的数据模型。而这一层的转换,是通过Gson的fromJson方法实现的。
//code1 Gson.java
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
if (json == null) {
return null;
}
StringReader reader = new StringReader(json);
T target = (T) fromJson(reader, typeOfT);
return target;
}
Gson支持以流的方式来读取字符,为了接口的复用性,如果你的第一个参数输入的是一个String对象,Gson一样会将它包装成为一个流对象StringReader。同时,我们也可以看到,实际上我们声明的泛型,并没有受到Type类型的不同而限制。什么意思呢?就是你完全可以写出以下这种形式的代码,也不会产生编译错(当然可能产生运行错):
Gson gson = new Gson();
String str = gson.fromJson("{}", Object.class);
Gson的fromJson会调用到统一的接口方法 T fromJson(JsonReader reader, Type typeOfT) 中去:
//code 2
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
boolean isEmpty = true;
boolean oldLenient = reader.isLenient();
reader.setLenient(true);//不严格按照Json标准语法
reader.peek();
isEmpty = false;
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
T object = typeAdapter.read(reader);
return object;
....
}
在code2中,代码一共做了以下几件事:
1.我们传入的一个StringReader,将会再次被包装成为一个Json语法格式的JsonReader对象。
2.通过调用setLenient方法,给Json语法提供更加大的宽容度
3.通过reader.peek方法,先做一次语法检查,并获取第一个非空字符,通过这个非空字符来标记下一个解析格式。
4.通过传入的类型Type,来获取这个类型对应的TypeToken
5.通过生成的TypeToken来生成对应的适配器
(上面我们说到,Gson的作用,是将Json数据模型往不同平台上的Java对象转换,实际上可以算是一种平台接口的转换,这里,Gson用了适配器的方式来实现这套方案)
我们来一步步地看下代码,代码一进来,reader就调用了一个peek操作,这是为什么呢?我们知道,在流式对象中,peek的目的是为了查看下一个字符,JsonReader也自然如此。JsonReader通过一次peek先做一个简单的语法校验,然后标注当前JsonReader解析的时候将以何种形式解析。
//code 3 JsonReader
public JsonToken peek() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
}
....
}
peek方法并不像我们之前的流式文件那样,返回一个真实的数据,而是返回一个当前的解析状态标识JsonToken。JsonToken是一个枚举类型,用来标识在Json数据格式中的各种语法单位。而在peek()函数的内部,又调用了一个doPeek()。虽然两个名字非常相同,但是返回的结果却是完全不同。doPeek是真是意义上的返回数据,也就是返回一个真实的字符:
//code doPeek()
private int doPeek() throws IOException {
int peekStack = stack[stackSize - 1];
....
if (peekStack == JsonScope.EMPTY_DOCUMENT) {//line 1
if (lenient) {
consumeNonExecutePrefix();
}
stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
...
}
....
int c = nextNonWhitespace(true);//line2
switch (c) {
....
case '{':
return peeked = PEEKED_BEGIN_OBJECT;//line3
default:
pos--; // Don't consume the first character in a literal value.
}
....
}
在doPeek()函数中,需要用到一个叫做stack变量的数据,里面存放的是叫做JsonScope的一堆常量。这是因为,Gson对Json的解析方式,是采用栈式的解析,按照层次解析的方式,这种层次解析的方式需要用栈来记录一些先前的状态。而这个stack的初始值,在一个初始代码块中设置:
private int[] stack = new int[32];
public int stackSize = 0;
{
stack[stackSize++] = JsonScope.EMPTY_DOCUMENT;
}
可见,stack初始栈顶数据为EMPTY_DOCUMENT常量。这时候,我们回到doPeek函数中去。我们可以看到:
1.if将调用line1中的方法体。将栈顶数据替换为NONEMPTY_DOCUMENT状态
2.在line2中获取下一个非空白字符,赋值给c变量
3.在line3中将执行case '{'将PEEKED_BEGIN_OBJECT 整型常量付给peeked变量,然后返回。
然后我们返回我们的peek()方法:
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
}
switch (p) {
case PEEKED_BEGIN_OBJECT:
return JsonToken.BEGIN_OBJECT;
}
doPeek方法返回一个整型的常量后,peek方法又通过这个整型常量转化为对应的JsonToken变量。或许有些看官就会问了,如果是这样的话,JsonToken的意义何在呢?你完全可以用返回的peeked变量来代替JsonToken的数据?
非墨暂时先不深入回答这个问题,而是通过代码来推测一下这种可能性:
case PEEKED_SINGLE_QUOTED_NAME:
case PEEKED_DOUBLE_QUOTED_NAME:
case PEEKED_UNQUOTED_NAME:
return JsonToken.NAME;
我们可以看到,三种peek出来的数据类型,实际上可能对应一种Json中的标识符,那么,也就是说peek出来的符号类型跟Json中的标识符,实际上是一种多对应的数据关系,所以不能混用。另外,我们在peek()方法中所生成的这个JsonToken,在Reader中并没有记录,也就是说,对于数据的解析,Reader还是关心doPeek方法中生成的中间状态数据。
我们回到code2中,根据上面的分析,我们已经知道reader.peek()方法实际上是初始了一些数据变量。接下去,fromJson方法将通过TypeToken.get的静态方法来生成一个TypeToken对象。
public static TypeToken<?> get(Type type) {
return new TypeToken<Object>(type);
}
TypeToken的get方式,是一种静态构造工厂,它将直接返回一个TypeToken对象。
TypeToken(Type type) {
this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type)/*验证*/);/*生成Gson内部的type*/
this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
this.hashCode = this.type.hashCode();
}
Gson代码结构中,以'$'打头的类用来标识那些只有静态方法的工具类:
1.$Gson$Preconditions:用于验证一些参数信息
2.$Gson$Types :相当于类适配器,将Java的Type实现类型转化为Gson自己的Type类型实现对象
在TypeToken中,有两个非常重要的变量,一个是type,一个是rawType。举个简单的例子来说明一下这两个变量的含义:
TypeToken token = new TypeToken<List<User>>(){}
我们通过一个匿名类来生成一个TypeToken对象,这里我们传入一个泛型的参数List<User>。那么这个TypeToken对应的type类型就是List<User>,而对应的rawType就是List。通过这个简单的理解,我们来看下Gson具体是如何做的:
//code $Gson$Types.java
public static Type canonicalize(Type type) {
if (type instanceof Class) {
Class<?> c = (Class<?>) type;
return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
} else if (type instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) type;
return new ParameterizedTypeImpl(p.getOwnerType(),
p.getRawType(), p.getActualTypeArguments());
} else if (type instanceof GenericArrayType) {
GenericArrayType g = (GenericArrayType) type;
return new GenericArrayTypeImpl(g.getGenericComponentType());
} else if (type instanceof WildcardType) {
WildcardType w = (WildcardType) type;
return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
} else {
// type is either serializable as-is or unsupported
return type;
}
}
Gson通过$Gson$Types的canonicalize方法,将原本定义在JDK里面的Type实现,转化为自己内部的数据实现。主要还是为了操作数据的方便。这里解释一下涉及泛型的一些函数:
1.getOwnerType: 代表拥有者类型,比如你的类型是一个内部类,那么这个Type就指向的是你外部类的类型
2.getRawType : 指向泛型的最外部的类型,比如List<User>指向的就是List类型
3.getGenericComponentType : 指向数组成员类型
4.Gson中将Array类型的Class也归纳为GenericArrayTypeImpl类型
5.getEnclosingClass: 获得外部类
我们重点来看一下ParameterizedType类型
public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
// require an owner type if the raw type needs it
if (rawType instanceof Class<?>) {
Class<?> rawTypeAsClass = (Class<?>) rawType;
boolean isStaticOrTopLevelClass = Modifier.isStatic(rawTypeAsClass.getModifiers())
|| rawTypeAsClass.getEnclosingClass() == null;
checkArgument(ownerType != null || isStaticOrTopLevelClass);//判断是否静态内部类
}
this.ownerType = ownerType == null ? null : canonicalize(ownerType);
this.rawType = canonicalize(rawType);
this.typeArguments = typeArguments.clone();
for (int t = 0; t < this.typeArguments.length; t++) {
checkNotNull(this.typeArguments[t]);
checkNotPrimitive(this.typeArguments[t]);
this.typeArguments[t] = canonicalize(this.typeArguments[t]);
}
}
在ParameterizedTypeImpl对象,构造的前半部分代码,将判断此类型是否是静态内部类。为什么需要做这一步操作呢?因为非静态内部类,需要通过外部类的对象才能生成,而Gson目前还没有传入一个外部类对象的接口。
之后,再生成一个外部类的Type变量ownerType,以及最外层类型rawType
之后,通过递归的方式生成typeArguments的类型
我们回到刚才user的例子:
Gson gson = new Gson();
String strJson = "{name:'david',age:19,room:{roomName:'small',number:1}}";
User u = gson.fromJson(strJson, User.class);
由于User类是一个很纯粹的类,因此,在canonicalize方法调用的时候,并不会构建Gson内部的类型,而是会直接返回Class类型。那么当TypeToken.get(User.class)的时候,返回的就是一个内部持有一个User.class为Type的TypeToken变量。
这时候我们再回到code2中(Gson.fromJson方法中),在通过TypeToken.get(Type)方法调用以后,将会通过一个叫getAdapter的方法来获得typeAdapter对象。
TypeAdapter是Gson代码体系中的重中之重,它承担了真正意义上的转换工作。Gson在Adapter的管理上,利用了享元,毕竟,解析类结构有一定的时间消耗,为了降低这种时间上的开销,Gson使用了缓冲池来管理这种映射关系。或许读者有一个问题:既然TypeToken.get方法每次都是利用静态工厂的方式构造一个新的TypeToken,那么你在放入cache中并不能起到cache的作用?
答案是:可以的。回答这个问题,我们需要回到TypeToken的源码:
@Override public final int hashCode() {
return this.hashCode;
}
@Override public final boolean equals(Object o) {
return o instanceof TypeToken<?>
&& $Gson$Types.equals(type, ((TypeToken<?>) o).type);
}
如上述代码显示,TypeToken利用相同的hashCode和复写的equals方法,来解决不同对象的冲突问题。好的,我们继续回到getAdapter方法:
// code Gson.getAdapter
Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
boolean requiresThreadLocalCleanup = false;
if (threadCalls == null) {
threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
calls.set(threadCalls);
requiresThreadLocalCleanup = true;
}
当cache中并不存在需要的适配器的时候,并且该线程并不存在有缓冲池对象的时候,Gson将构造一个Map对象,放入线程的中。同事我们还可以发现Gson的Cache的特点:
1.在Gson的Cache管理里,实际上应用了二级缓冲
2.Gson对象内部有一个缓冲,而Gson对象所在的线程又有一个共享Cache。
3.在不同的线程之间,又使用不同的cache。
是否有某位看官有这样的疑问,为什么在Gson的管理里会有3级的Cache?
首先,为什么Gson自身有一个Cache呢?明明可以直接从线程Cache中获取?
这个是因为Gson可能被另外一个线程访问,而如果有了Gson的对象Cache就可以实现共享。
为什么需要多线程之间的Cache?
这个问题非墨是基于一种猜测,多线程间的Cache不一定可以省略,原因是因为如果Adapter在转换数据过程中存在有状态位,很有可能造成数据不同步(但其实这个,完全可以通过拷贝的方式解决。这对提升Gson的效率是很有帮助的)
getAdapter方法之后是方法的核心:
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
threadCalls.put(type, call);
for (TypeAdapterFactory factory : factories) {
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
call.setDelegate(candidate);
typeTokenCache.put(type, candidate);
return candidate;
}
}
Adapter的构造是通过遍历一个factories集合类来完成的。做法也很简单,Factory通过匹配传入的TypeToken类型,然后是否匹配,如果匹配将生成对象,不匹配就返回null。同时,一旦生成Adapter对象,程序立马返回,说明构造对象是有优先级别的,可能你的AFactory通过规则可以生成对象,BFactory通过规则也可以生成对象,那么这个时候如果AFactory在BFactory的前面,那么将优先使用AFactory来构造Adapter。
factories的初始化数据在Gson的构造器里:
// built-in type adapters that cannot be overridden
factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);//针对JsonElement类做特别处理
factories.add(ObjectTypeAdapter.FACTORY);//针对Object类做特殊处理
// the excluder must precede all adapters that handle user-defined types
factories.add(excluder);//用于拦截一些不在使用的类型
// user's type adapters
factories.addAll(typeAdapterFactories);
....
this.factories = Collections.unmodifiableList(factories);
我们不妨挑一些工厂来看一下,我们来看下excluder这个工厂。excluder是用于拦截一些过期的Gson类型。它的实现类是:Excluder类:
//code Excluder.java
// function:public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type)
Class<?> rawType = type.getRawType();
final boolean skipSerialize = excludeClass(rawType, true);
final boolean skipDeserialize = excludeClass(rawType, false);
if (!skipSerialize && !skipDeserialize) {
return null;
}
代码中可以看出,Excluder类是通过excludeClass方法,来判断是否exclude掉该类,我们再看一下excludeClass方法:
public boolean excludeClass(Class<?> clazz, boolean serialize) {
if (version != Excluder.IGNORE_VERSIONS
&& !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {//判断类注解
return true;
}
if (!serializeInnerClasses && isInnerClass(clazz)) {//判断是否是内部类,并且增加忽略内部类标注
return true;
}
if (isAnonymousOrLocal(clazz)) {//判断是否是匿名类,或者是局部类
return true;
}
List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
for (ExclusionStrategy exclusionStrategy : list) {//通过判断策略来判断是否忽略类型
if (exclusionStrategy.shouldSkipClass(clazz)) {
return true;
}
}
return false;
}
Gson将会用一堆的策略接口,来判断你的类是否被skip。当Excluder 确定skip掉你的类的时候,excludeClass将返回true结果。代码执行之后,将返回一个匿名的TypeAdapter对象:
return new TypeAdapter<T>() {
/** The delegate is lazily created because it may not be needed, and creating it may fail. */
private TypeAdapter<T> delegate;
@Override public T read(JsonReader in) throws IOException {
if (skipDeserialize) {
in.skipValue();
return null;
}
return delegate().read(in);
}
@Override public void write(JsonWriter out, T value) throws IOException {
if (skipSerialize) {
out.nullValue();
return;
}
delegate().write(out, value);
}
private TypeAdapter<T> delegate() {
TypeAdapter<T> d = delegate;
return d != null
? d
: (delegate = gson.getDelegateAdapter(Excluder.this, type));
}
};
}
而这个匿名的对象,将通过一个代理对象delegate(通过gson.getDelegateAdapter(Excluder.this, type)获得)来适配对象。
我们回到上面User和ClassRoom的例子,当我们传入一个User.class类型的时候,Gson会通过ReflectiveTypeAdapterFactory工厂来生产一个Adapter来适配Json对象:
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
Class<? super T> raw = type.getRawType();
if (!Object.class.isAssignableFrom(raw)) {//拦截基本类型
return null; // it's a primitive!
}
ObjectConstructor<T> constructor = constructorConstructor.get(type);//获取构造器
return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
}
我们可以通过上面的代码得到以下的结论:
1.这个工厂类的将针对所有非基本类型的类型进行拦截,然后通过内部的Adapter类进行转换
2.而在转换的过程中,需要传入一个ObjectConstructor类型的对象用于构造对象。而这个构造器的获取,源自于一个constructorConstructor的变量。
这里,我们应该不论从名字上还是从什么方面,都能很清楚的知道ObjectConstructor和constructorConstructor的用意。
那么为什么需要一个Constructor的构造器呢?你直接通过类型反射不就完成类型的构造?
原因是因为Gson是支持接口类型,比如List<?>这种集合类。而接口类型转化为Java的实际对象的方式主要有两种:
一种是常见的提供实现类,另一种是生成接口类型的动态代理。而通过动态代理的方案明显不合适,因此Gson在处理这种接口类型的时候,默认提供了一些实现类。
constructorConstructor对象在ReflectiveTypeAdapterFactory构造的时候传入,而所有TypeAdapter的构造工厂都在Gson对象初始化的时候构造。
Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy,
final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
boolean prettyPrinting, boolean serializeSpecialFloatingPointValues,
LongSerializationPolicy longSerializationPolicy,
List<TypeAdapterFactory> typeAdapterFactories) {
this.constructorConstructor = new ConstructorConstructor(instanceCreators);
...
}
constructorConstructor的构造,传入了一个instanceCreators对象,实际上,这个对象是一个享元对象。但默认的情况下,这个对象为空,如果你有自己构造一个对象的需求,可以注入这个对象。那么,Gson会优先选择你注入的这个对象构造器来改造最终对象,为了说明这点,我们来看下ConstructorConstructor的
ObjectConstructor<T> get(TypeToken<T> typeToken)方法:
public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
final Type type = typeToken.getType();
final Class<? super T> rawType = typeToken.getRawType();
// first try an instance creator
@SuppressWarnings("unchecked") // types must agree
final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);//先在instanceCreators 查找是否有对应的构造器
if (typeCreator != null) {
return new ObjectConstructor<T>() {
public T construct() {
return typeCreator.createInstance(type);
}
};
}
// Next try raw type match for instance creators
@SuppressWarnings("unchecked") // types must agree
final InstanceCreator<T> rawTypeCreator =
(InstanceCreator<T>) instanceCreators.get(rawType);
if (rawTypeCreator != null) {
return new ObjectConstructor<T>() {
public T construct() {
return rawTypeCreator.createInstance(type);
}
};
}
//如果没有找到构造器,将使用默认构造器
ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
if (defaultConstructor != null) {
return defaultConstructor;
}
ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
if (defaultImplementation != null) {
return defaultImplementation;
}
// finally try unsafe
return newUnsafeAllocator(type, rawType);
}
代码刚开始,将通过instanceCreators拦截,一旦拦截失败,如果是普通类,将使用默认构造器,如果是接口类将通过newDefaultImplementationConstructor方法,来获取默认接口实现类。
private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
try {
final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return new ObjectConstructor<T>() {
@SuppressWarnings("unchecked") // T is the same raw type as is requested
public T construct() {
try {
Object[] args = null;
return (T) constructor.newInstance(args);
....
}
}
可见,普通类的构造,Gson将通过一个无参数的构造器来反射调用。而非普通类型将调用newDefaultImplementationConstructor方法来生成一些默认的构造器
private <T> ObjectConstructor<T> newDefaultImplementationConstructor(
final Type type, Class<? super T> rawType) {//集合类检测
if (Collection.class.isAssignableFrom(rawType)) {
if (SortedSet.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new TreeSet<Object>();//排序集合实现类
}
};
} else if (EnumSet.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
@SuppressWarnings("rawtypes")
public T construct() {
if (type instanceof ParameterizedType) {
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
if (elementType instanceof Class) {
return (T) EnumSet.noneOf((Class)elementType);
} else {
throw new JsonIOException("Invalid EnumSet type: " + type.toString());
}
} else {
throw new JsonIOException("Invalid EnumSet type: " + type.toString());
}
}
};
} else if (Set.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new LinkedHashSet<Object>();//集合类默认实现
}
};
} else if (Queue.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new LinkedList<Object>();//队列默认实现
}
};
} else {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new ArrayList<Object>();//否则统一用ArrayList来实现集合类
}
};
}
}
if (Map.class.isAssignableFrom(rawType)) {
if (SortedMap.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new TreeMap<Object, Object>();
}
};
} else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom(
TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new LinkedHashMap<Object, Object>();
}
};
} else {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new LinkedTreeMap<String, Object>();
}
};
}
}
return null;
}
我们今天先初探到这里,后面的文章,非墨会深入到Gson是如何解析的。