上篇文章<轻触开源-Java泛型Type类型的应用和实践(一)>
https://my.oschina.net/u/874727/blog/747427
非墨写到JAVA的泛型机制,被用到很多的开源项目。在众多的开源项目中,Gson是很具有代表性的一个。Gson是Google公司编写的一套用于Json数据转化为Java对象的一套通用工具库。之所以说它是通用工具库,是因为它的实现代码全部基于最基础的Java运行时环境,而不依赖于任何系统平台,也就是说你不仅可以在J2EE项目中应用它,你一样可以很容易的在Android,J2ME等等平台中直接应用它。
Gson跟很多的开源操纵了Java内部数据类型的项目相同,为了方便记录类型数据,Gson会将Java原有的一套数据类型,转化为自己的内部数据类型。比如,在上一章我们提到的在Java泛型中记录类型的Class和Type类型,就被Gson转化为TypeToken。WildcardType转化为Gson自己的WildcardTypeImpl,GenericArrayType转为了Gson的内部类型GenericArrayTypeImpl。而这些类型的定义都被记录在com.google.gson.internal包中。我们从这个包名也看的很明白,就是Gson系统将一些转换的细节屏蔽到Gson项目的内部,而只暴露给用户一些简单的接口。
但不论Gson如何转变既定的Java类型,实际上都只是在Java的既定类型外加一层壳,可以说是一个类适配器,比如我们来看一下WildcardTypeImpl的代码:
private static final class WildcardTypeImpl implements WildcardType, Serializable {
private final Type upperBound;
private final Type lowerBound;
public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
checkArgument(lowerBounds.length <= 1);
checkArgument(upperBounds.length == 1);
if (lowerBounds.length == 1) {
checkNotNull(lowerBounds[0]);
checkNotPrimitive(lowerBounds[0]);
checkArgument(upperBounds[0] == Object.class);
this.lowerBound = canonicalize(lowerBounds[0]);
this.upperBound = Object.class;
} else {
checkNotNull(upperBounds[0]);
checkNotPrimitive(upperBounds[0]);
this.lowerBound = null;
this.upperBound = canonicalize(upperBounds[0]);
}
}
public Type[] getUpperBounds() {
return new Type[] { upperBound };
}
public Type[] getLowerBounds() {
return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY;
}
@Override public boolean equals(Object other) {
return other instanceof WildcardType
&& $Gson$Types.equals(this, (WildcardType) other);
}
@Override public int hashCode() {
// this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());
return (lowerBound != null ? 31 + lowerBound.hashCode() : 1)
^ (31 + upperBound.hashCode());
}
@Override public String toString() {
if (lowerBound != null) {
return "? super " + typeToString(lowerBound);
} else if (upperBound == Object.class) {
return "?";
} else {
return "? extends " + typeToString(upperBound);
}
}
private static final long serialVersionUID = 0;
}
WildcardType对象中最重要的upper和lower参数,实际上都是由外部对象传入,在Gson的WildcardTypeImpl内部,不过是做了一层适配器。好的,我们先预热到这里,我们进入我们今天的主题Gson的源码。
在我们深入讲Gson源码之前,我们先用一下Gson这个库,领略一下它的魅力。因此我们先构建两个基础的Java模型:
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字符:
String strJson = "{name:'david',age:19,room:{roomName:'small',number:1}}";
User u = gson.fromJson(strJson, User.class);
最后我们可以将生成的u对象打印一下得到:
david->19:[small:1]
各位看官是否被惊艳到?是的,使用Gson就是可以这么容易的转换Json对象。虽然我们还没开始阅读Gson的源代码,但是我们可以从传入的参数简单看出,在Gson的实现中,一定是大量用到了Java的反射注入技术。我们看下Gson的分包:
gson的分包很简单,从名字上看,每个包分类的目的也都很明确。在Gson中,从普通的Json对象到Gson对象的转换,是通过internal包及其子包bind中的适配器TypeAdapter完成的。而这种完成的类型数据,是依赖于reflect中记录的Type信息来完成的。适配器所需要的输入源或者输出源,是来自于stream包的数据流。当你的对象模型有一些特殊的输出需求或者输入需求,可以通过annotation包中的注解来操纵你的元数据。为了说明这一切,我们回头看一下我们的测试代码,在代码中,我们是直接调用了Gson.fromJson方法。当我们跟踪fromJson这个方法到最后,我们会发现Gson.fromJson方法最终会调用到方法块:
//file:"com/google/gson/Gson.java"
@SuppressWarnings("unchecked")
public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
JsonReader jsonReader = new JsonReader(json);// step1
T object = (T) fromJson(jsonReader, typeOfT);
assertFullConsumption(object, jsonReader);
return object;
}
@SuppressWarnings("unchecked")
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
boolean isEmpty = true;
boolean oldLenient = reader.isLenient();
reader.setLenient(true);
try {
reader.peek();//step2
isEmpty = false;
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);//step3
TypeAdapter<T> typeAdapter = getAdapter(typeToken);//step4
T object = typeAdapter.read(reader);
return object;
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return null for empty
* documents instead of throwing.
*/
if (isEmpty) {
return null;
}
throw new JsonSyntaxException(e);
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
// TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException
throw new JsonSyntaxException(e);
} finally {
reader.setLenient(oldLenient);
}
}
就像我们上面说的一样,在代码#step1 中,Gson会将真实的字符IO流Reader装饰成为在stream包下的JsonReader流。在代码#step3位置,Gson会通过Java既定的类型找到Gson所转换的type类型(reflect包下的TypeToken对象)。然后通过这个类型调用代码#step4的语句,获取一个类型转换的适配器TypeAdapter。适配器获取到Reader输入源之后,就可以将Json数据转化成为对应的对象。
TypeToken采用一种懒加载的机制来生成TypeToken。这种机制在程序代码中非常常见。
/**
* Gets type literal for the given {@code Type} instance.
*/
public static TypeToken<?> get(Type type) {
return new TypeToken<Object>(type);
}
@SuppressWarnings("unchecked")
protected TypeToken() {
this.type = getSuperclassTypeParameter(getClass());
this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
this.hashCode = type.hashCode();
}
/**
* Unsafe. Constructs a type literal manually.
*/
@SuppressWarnings("unchecked")
TypeToken(Type type) {
this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
this.hashCode = this.type.hashCode();
}
我们可以看到,TypeToken.get方法,实际上是生成了一个TypeToken对象。而对于TypeToken对象的生成,在TypeToken类中有两种构造方式。第一种无参数的构造方式的作用域设置为protected,意味着你必须要通过继承的方式才能使用它,并且所需要转化的类型需要通过继承里的泛型参数指定。而第二种构造方法需要传入一个Type对象。而这个Type对象就是我们上一篇文章中的Type对象(四种直接子接口和一个实现类)。而Java中的Type类型到Gson中的对象映射,就由$Gson$Type的canonicalize方法完成:
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在将数据源传入给适配器Adapter做转换操作的之前,会有一个peek操作。peek跟我们平常用到的流的peek没有什么差异,都是不弹出流数据的情况下查看缓冲区数据。这个peek操作的目的是为了确定以何种输入源类型来处理Json对象。比如,当你输入的JSON串为:
{name:david}
的时候,由于输入员peek出来的是一个"{"字符。在JsonReader对象中,会以"PEEKED_BEGIN_OBJECT" 变量标记peek状态量。而如果你的Json数据是:
[{name:david},{name:Lily}]
的话,由于JsonReader.peek出来的数据是"["字符,因此peek状态量会标记为"PEEKED_BEGIN_ARRAY"。
public JsonToken peek() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
}
....
default:
throw new AssertionError();
}
}
由于JsonReader我们是刚刚生成,因此peeked状态量的默认值,也就是PEEKED_NONE.这样,程序就跳转到函数doPeek()中。
private int doPeek() throws IOException {
int peekStack = stack[stackSize - 1];//状态栈
...
}
JsonReader使用栈式的解析,stack存放JsonScope常量所枚举的对象。这个栈中主要存放解析过程中所操纵的对象类型。由于目前Json解析尚未开始,目前栈中存放的是默认值"EMPTY_DOCUMENT"
final class JsonScope {
/**
* An array with no elements requires no separators or newlines before
* it is closed.
*/
static final int EMPTY_ARRAY = 1;
/**
* A array with at least one value requires a comma and newline before
* the next element.
*/
static final int NONEMPTY_ARRAY = 2;
/**
* An object with no name/value pairs requires no separators or newlines
* before it is closed.
*/
static final int EMPTY_OBJECT = 3;
/**
* An object whose most recent element is a key. The next element must
* be a value.
*/
static final int DANGLING_NAME = 4;
/**
* An object with at least one name/value pair requires a comma and
* newline before the next element.
*/
static final int NONEMPTY_OBJECT = 5;
/**
* No object or array has been started.
*/
static final int EMPTY_DOCUMENT = 6;
/**
* A document with at an array or object.
*/
static final int NONEMPTY_DOCUMENT = 7;
/**
* A document that's been closed and cannot be accessed.
*/
static final int CLOSED = 8;
}
之后根据读入的第一个字符"{"或者"["返回具体的类型,对于"{"字符,将返回一个"PEEKED_BEGIN_OBJECT"类型。
private void doPeek() {
...
int c = nextNonWhitespace(true);//取得第一个非空白字符
switch (c) {
...
case '[':
return peeked = PEEKED_BEGIN_ARRAY;
case '{':
return peeked = PEEKED_BEGIN_OBJECT;
}
...
}
但从代码功能上来看,实际上讲Reader.peek代码放在Adapter.read代码前的任何位置都不影响逻辑。我们再继续之前的代码段:
//com.google.gson.Gson fromJson()
1.reader.peek();
2.isEmpty = false;
3.TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
4.TypeAdapter<T> typeAdapter = getAdapter(typeToken);
5.T object = typeAdapter.read(reader);
6.return object;
第1行代码,我们通过peek来记录一下Json的最外层对象类型
第3行代码,我们用过传入的类型来生成了一个Gson的类型对象TypeToken
第4行代码,我们通过第三行代码生成的TypeToken对象生成一个数据转换的适配器
第5行代码,我们通过适配器,将输入源中的Json数据转换为Java中的数据对象
上面我们已经说到了第三行代码,TypeToken.get方法调用后,new了一个TypeToken对象。
// code1
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
TypeAdapter<?> cached = typeTokenCache.get(type);//#1 缓存TypeAdapter
if (cached != null) {
return (TypeAdapter<T>) cached;
}
Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();//#2 线程安全
boolean requiresThreadLocalCleanup = false;
if (threadCalls == null) {
threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
calls.set(threadCalls);
requiresThreadLocalCleanup = true;
}
// the key and value type parameters always agree
FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
}
try {
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();//#3无用的类装饰
threadCalls.put(type, call);
for (TypeAdapterFactory factory : factories) {
TypeAdapter<T> candidate = factory.create(this, type);//#4查找对应的工厂
if (candidate != null) {
call.setDelegate(candidate);
typeTokenCache.put(type, candidate);
return candidate;
}
}
throw new IllegalArgumentException("GSON cannot handle " + type);
} finally {
threadCalls.remove(type);
if (requiresThreadLocalCleanup) {
calls.remove();
}
}
}
Gson在获取TypeAdapter的时候,会先从线程的Cache中去取,代码#1很好的诠释了这一点。而为了保证在多线程状态下的状态稳定性,Gson给每个线程都设定了一个Map类型的Cache。#2之后的代码就是在完成这么一项工作。在#3代码里Gson引入了一个新的类FutureTypeAdapter。这个类实际上没有什么实际上的意义,所以可以忽略它。在代码#4的时候,Gson通过遍历自己的factories列表来生成一个TypeAdapter对象。实际上在这一步,Gson在做一个Factory选择,我们来看一个Factory的例子:
// code CollectionTypeAdapterFactory.java
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType();
if (!Collection.class.isAssignableFrom(rawType)) { #1 集合类判断
return null;
}
...
TypeAdapter<T> result = new Adapter(gson, elementType, elementTypeAdapter, constructor);
return result;
}
这是一个集合类TypeAdapter生成的例子。代码#1就是通过判断传入的对象类型是否是集合类型来进行选择。
(待续)
来源:oschina
链接:https://my.oschina.net/u/874727/blog/749405