上篇文章<輕觸開源-Java泛型Type類型的應用和實踐(一)>java
http://www.javashuo.com/article/p-pzkygieh-dn.htmljson
非墨寫到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的源碼。ide
在咱們深刻講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的分包:ui
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就是經過判斷傳入的對象類型是不是集合類型來進行選擇。
(待續)